diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 00000000..23d3744b --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,13 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "csharpier": { + "version": "1.1.2", + "commands": [ + "csharpier" + ], + "rollForward": false + } + } +} diff --git a/.csharpierignore b/.csharpierignore new file mode 100644 index 00000000..e69fe35c --- /dev/null +++ b/.csharpierignore @@ -0,0 +1,2 @@ +*.csproj +*.props diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..e18ab355 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,12 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/dotnet +{ + "name": "C# (.NET)", + "image": "mcr.microsoft.com/devcontainers/dotnet:1-8.0-noble", + "postAttachCommand": "cat .vscode/extensions.json | jq -r .recommendations[] | xargs -n 1 code --install-extension", + "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", + "features": { + // For the mock server. + "ghcr.io/devcontainers/features/node:1": {} + } +} diff --git a/.editorconfig b/.editorconfig index 31f2b2a1..81b1c6bc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,229 +1,36 @@ -[*.cs] - -# SA1600: Elements should be documented -dotnet_diagnostic.SA1600.severity = none - -# SA1117: Parameters should be on same line or separate lines -dotnet_diagnostic.SA1117.severity = none - -# SA1614: Element parameter documentation should have text -dotnet_diagnostic.SA1614.severity = none - -# SA1606: Element documentation should have summary text -dotnet_diagnostic.SA1606.severity = none - -# SA1402: File may only contain a single type -dotnet_diagnostic.SA1402.severity = none - -[*.cs] -#### Naming styles #### - -# Naming rules - -dotnet_naming_rule.interface_should_be_begins_with_i.severity = warning -dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface -dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i - -dotnet_naming_rule.types_should_be_pascal_case.severity = warning -dotnet_naming_rule.types_should_be_pascal_case.symbols = types -dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case - -dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning -dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members -dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case - -# Symbol specifications - -dotnet_naming_symbols.interface.applicable_kinds = interface -dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.interface.required_modifiers = - -dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum -dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.types.required_modifiers = - -dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method -dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.non_field_members.required_modifiers = - -# Naming styles - -dotnet_naming_style.begins_with_i.required_prefix = I -dotnet_naming_style.begins_with_i.required_suffix = -dotnet_naming_style.begins_with_i.word_separator = -dotnet_naming_style.begins_with_i.capitalization = pascal_case - -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = -dotnet_naming_style.pascal_case.capitalization = pascal_case - -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = -dotnet_naming_style.pascal_case.capitalization = pascal_case -csharp_using_directive_placement = outside_namespace:silent -csharp_prefer_simple_using_statement = true:suggestion -csharp_prefer_braces = true:silent -csharp_style_namespace_declarations = block_scoped:silent -csharp_style_prefer_method_group_conversion = true:silent -csharp_style_expression_bodied_methods = false:silent -csharp_style_expression_bodied_constructors = false:silent -csharp_style_expression_bodied_operators = false:silent -csharp_style_expression_bodied_properties = true:silent -csharp_style_expression_bodied_indexers = true:silent -csharp_style_expression_bodied_accessors = true:silent -csharp_style_expression_bodied_lambdas = true:silent -csharp_style_expression_bodied_local_functions = false:silent -csharp_indent_labels = one_less_than_current -csharp_space_around_binary_operators = before_and_after -csharp_style_throw_expression = true:suggestion -csharp_style_prefer_null_check_over_type_check = true:suggestion -csharp_prefer_simple_default_expression = true:suggestion -csharp_style_prefer_local_over_anonymous_function = true:suggestion -csharp_style_prefer_index_operator = true:suggestion -csharp_style_prefer_range_operator = true:suggestion -csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion -csharp_style_prefer_tuple_swap = true:suggestion -csharp_style_inlined_variable_declaration = true:suggestion -csharp_style_deconstructed_variable_declaration = true:suggestion -csharp_style_unused_value_assignment_preference = discard_variable:suggestion -csharp_style_unused_value_expression_statement_preference = discard_variable:silent -csharp_prefer_static_local_function = true:suggestion -csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent -csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent -csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent -csharp_style_conditional_delegate_call = true:suggestion -csharp_style_prefer_parameter_null_checking = true:suggestion -csharp_style_prefer_switch_expression = true:suggestion -csharp_style_prefer_pattern_matching = true:silent -csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion -csharp_style_pattern_matching_over_as_with_null_check = true:suggestion -csharp_style_prefer_not_pattern = true:suggestion -csharp_style_prefer_extended_property_pattern = true:suggestion -csharp_style_var_for_built_in_types = false:none -csharp_style_var_when_type_is_apparent = false:none -csharp_style_var_elsewhere = false:none - -# CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. -dotnet_diagnostic.CS8632.severity = none - -# SA1310: Field names should not contain underscore -dotnet_diagnostic.SA1310.severity = none - -# IDE0051: Remove unused private members -dotnet_diagnostic.IDE0051.severity = none - -# SA1300: Element should begin with upper-case letter -dotnet_diagnostic.SA1300.severity = none - -# IDE1006: Naming Styles -dotnet_diagnostic.IDE1006.severity = none - -# SA1307: Accessible fields should begin with upper-case letter -dotnet_diagnostic.SA1307.severity = none - -# SA1202: Elements should be ordered by access -dotnet_diagnostic.SA1202.severity = none - -# SA1201: Elements should appear in the correct order -dotnet_diagnostic.SA1201.severity = none - -# SA1306: Field names should begin with lower-case letter -dotnet_diagnostic.SA1306.severity = none - -# IDE0008: Use explicit type -dotnet_diagnostic.IDE0008.severity = none - -# SA1116: Split parameters should start on line after declaration -dotnet_diagnostic.SA1116.severity = none - -[*.vb] -#### Naming styles #### - -# Naming rules - -dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion -dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface -dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i - -dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.types_should_be_pascal_case.symbols = types -dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case - -dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members -dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case - -# Symbol specifications - -dotnet_naming_symbols.interface.applicable_kinds = interface -dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected -dotnet_naming_symbols.interface.required_modifiers = - -dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum -dotnet_naming_symbols.types.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected -dotnet_naming_symbols.types.required_modifiers = - -dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method -dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected -dotnet_naming_symbols.non_field_members.required_modifiers = - -# Naming styles - -dotnet_naming_style.begins_with_i.required_prefix = I -dotnet_naming_style.begins_with_i.required_suffix = -dotnet_naming_style.begins_with_i.word_separator = -dotnet_naming_style.begins_with_i.capitalization = pascal_case - -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = -dotnet_naming_style.pascal_case.capitalization = pascal_case - -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = -dotnet_naming_style.pascal_case.capitalization = pascal_case - -[*.{cs,vb}] -dotnet_style_operator_placement_when_wrapping = beginning_of_line -tab_width = 4 -indent_size = 4 -end_of_line = crlf - -# Default severity for analyzer diagnostics with category 'StyleCop.CSharp.DocumentationRules' -dotnet_analyzer_diagnostic.category-StyleCop.CSharp.DocumentationRules.severity = none -dotnet_style_coalesce_expression = true:suggestion -dotnet_style_null_propagation = true:suggestion -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion -dotnet_style_prefer_auto_properties = true:none -dotnet_style_object_initializer = true:suggestion -dotnet_style_collection_initializer = true:suggestion -dotnet_style_prefer_simplified_boolean_expressions = true:suggestion -dotnet_style_prefer_conditional_expression_over_assignment = true:silent -dotnet_style_prefer_conditional_expression_over_return = true:silent -dotnet_style_explicit_tuple_names = true:suggestion -dotnet_style_prefer_inferred_tuple_names = true:suggestion -dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion -dotnet_style_prefer_compound_assignment = true:suggestion -dotnet_style_prefer_simplified_interpolation = true:suggestion -dotnet_style_namespace_match_folder = true:suggestion -dotnet_style_readonly_field = true:suggestion -dotnet_style_predefined_type_for_locals_parameters_members = true:silent -dotnet_style_predefined_type_for_member_access = true:silent -dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent -dotnet_style_allow_multiple_blank_lines_experimental = true:silent -dotnet_style_allow_statement_immediately_after_block_experimental = true:silent -dotnet_code_quality_unused_parameters = all:suggestion -dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent -dotnet_style_qualification_for_field = false:silent -dotnet_style_qualification_for_property = false:silent -dotnet_style_qualification_for_method = false:silent -dotnet_style_qualification_for_event = false:silent - -# IDE0032: Use auto property -dotnet_diagnostic.IDE0032.severity = none +root = true + +# All files +[*] +indent_style = space + +# Xml files +[*.xml] +indent_size = 2 + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +tab_width = 4 + +# New line preferences +end_of_line = crlf +insert_final_newline = true + +dotnet_diagnostic.IDE0060.severity = none # Caused by resource with no methods and no subresources +dotnet_diagnostic.IDE1006.severity = none # Some names may not match up with C# conventions +dotnet_diagnostic.IDE0290.severity = none # Don't prefer primary constructors +dotnet_diagnostic.IDE0028.severity = none # "Collection initialization can be simplified" is a bit overzealous +dotnet_diagnostic.IDE0090.severity = none # "Simplify 'new' expression" is a bit overzealous +dotnet_diagnostic.IDE0059.severity = none # Sometimes we create "unnecessary" variables for clarity + +# For .NET Standard 2.0 support +dotnet_diagnostic.IDE0057.severity = none # Caused by use of `.Substring(...)` +dotnet_diagnostic.CA1866.severity = none # Caused by use of `.StartsWith(...)` with single character string +dotnet_diagnostic.CA1847.severity = none # Caused by use of `.Contains(...)` with single character string +dotnet_diagnostic.CA2263.severity = none # Caused by use of non-generic `Enum.IsDefined(...)` +dotnet_diagnostic.SYSLIB1045.severity = none # GeneratedRegex not available in netstandard2.0 diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 1ff0c423..00000000 --- a/.gitattributes +++ /dev/null @@ -1,63 +0,0 @@ -############################################################################### -# Set default behavior to automatically normalize line endings. -############################################################################### -* text=auto - -############################################################################### -# Set default behavior for command prompt diff. -# -# This is need for earlier builds of msysgit that does not have it on by -# default for csharp files. -# Note: This is only used by command line -############################################################################### -#*.cs diff=csharp - -############################################################################### -# Set the merge driver for project and solution files -# -# Merging from the command prompt will add diff markers to the files if there -# are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following -# file extensions to fail to load in VS. An alternative would be to treat -# these files as binary and thus will always conflict and require user -# intervention with every merge. To do so, just uncomment the entries below -############################################################################### -#*.sln merge=binary -#*.csproj merge=binary -#*.vbproj merge=binary -#*.vcxproj merge=binary -#*.vcproj merge=binary -#*.dbproj merge=binary -#*.fsproj merge=binary -#*.lsproj merge=binary -#*.wixproj merge=binary -#*.modelproj merge=binary -#*.sqlproj merge=binary -#*.wwaproj merge=binary - -############################################################################### -# behavior for image files -# -# image files are treated as binary by default. -############################################################################### -#*.jpg binary -#*.png binary -#*.gif binary - -############################################################################### -# diff behavior for common document formats -# -# Convert binary document formats to text before diffing them. This feature -# is only available from the command line. Turn it on by uncommenting the -# entries below. -############################################################################### -#*.doc diff=astextplain -#*.DOC diff=astextplain -#*.docx diff=astextplain -#*.DOCX diff=astextplain -#*.dot diff=astextplain -#*.DOT diff=astextplain -#*.pdf diff=astextplain -#*.PDF diff=astextplain -#*.rtf diff=astextplain -#*.RTF diff=astextplain diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..23c20df9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,69 @@ +name: CI +on: + push: + branches: + - '**' + - '!integrated/**' + - '!stl-preview-head/**' + - '!stl-preview-base/**' + - '!generated' + - '!codegen/**' + - 'codegen/stl/**' + pull_request: + branches-ignore: + - 'stl-preview-head/**' + - 'stl-preview-base/**' + +jobs: + lint: + timeout-minutes: 10 + name: lint + runs-on: ${{ github.repository == 'stainless-sdks/imagekit-diversion-csharp' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Set up .NET + uses: actions/setup-dotnet@55ec9447dda3d1cf6bd587150f3262f30ee10815 # v3.4.2 + with: + dotnet-version: '8.0.x' + + - name: Run bootstrap + run: ./scripts/bootstrap + + - name: Run lints + run: ./scripts/lint + build: + timeout-minutes: 10 + name: build + runs-on: ${{ github.repository == 'stainless-sdks/imagekit-diversion-csharp' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Set up .NET + uses: actions/setup-dotnet@55ec9447dda3d1cf6bd587150f3262f30ee10815 # v3.4.2 + with: + dotnet-version: '8.0.x' + + - name: Build SDK + run: ./scripts/build + + test: + timeout-minutes: 10 + name: test + runs-on: ${{ github.repository == 'stainless-sdks/imagekit-diversion-csharp' && 'depot-windows-2022' || 'windows-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Set up .NET + uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 + with: + dotnet-version: '8.0.x' + + - name: Run tests + run: ./scripts/test + shell: bash diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml deleted file mode 100644 index 794d8934..00000000 --- a/.github/workflows/dotnet-core.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: CI Pipeline - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - release: - types: - - published -env: - # Disable the .NET logo in the console output. - DOTNET_NOLOGO: true - # Stop wasting time caching packages - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true - # Disable sending usage data to Microsoft - DOTNET_CLI_TELEMETRY_OPTOUT: true - GITHUB_FEED: https://nuget.pkg.github.com/cloudmeteor/index.json - GITHUB_USER: cloudmeteor - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NUGET_FEED: https://api.nuget.org/v3/index.json - NUGET_KEY: ${{ secrets.NUGET_APIKEY }} - ATTRIBUTE_NAME: nupkg - NUGET_OUTPUT_PATH: nuget-package - Solution_Name: Imagekit.sln - -jobs: - build: - name: Build and test - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@master - - - name: Setup .NET - uses: actions/setup-dotnet@v1 - with: - dotnet-version: | - 2.1.x - 3.1.x - 5.0.x - 6.0.x - 7.0.x - 8.0.x - - name: Build Release - run: dotnet build Imagekit.sln -c Release /p:ContinuousIntegrationBuild=true - - # run: dotnet test --no-build Imagekit.UnitTests/Imagekit.UnitTests.csproj -c Release - - name: Run test suite - run: dotnet test Imagekit.UnitTests/Imagekit.UnitTests.csproj --verbosity minimal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover - - - name: Upload Coverage to Codecov - uses: codecov/codecov-action@v1 - with: - file: ./Imagekit.UnitTests/coverage.net6.0.opencover.xml - - - name: Pack - run: dotnet pack Imagekit.sln -c Release --no-build --output ${{ env.NUGET_OUTPUT_PATH }} - - - name: 'Upload Artifact' - uses: actions/upload-artifact@v3 - with: - name: ${{ env.ATTRIBUTE_NAME }} - path: ${{ env.NUGET_OUTPUT_PATH }} - - Publish: - needs: build - if: (github.event_name == 'release') || (github.ref == 'refs/tags/*') - runs-on: ubuntu-latest - steps: - - name: Download all workflow run artifacts - uses: actions/download-artifact@v3 - with: - name: ${{ env.ATTRIBUTE_NAME }} - - - name: Setup .NET - uses: actions/setup-dotnet@v1 - with: - dotnet-version: 6.0.x - - - name: Publish NuGet packages to NuGet - run: dotnet nuget push "*.nupkg" -k ${{ secrets.NUGET_APIKEY }} --source ${{ env.NUGET_FEED }} --no-symbols true --skip-duplicate diff --git a/.github/workflows/publish-nuget.yml b/.github/workflows/publish-nuget.yml new file mode 100644 index 00000000..5e8a0eb5 --- /dev/null +++ b/.github/workflows/publish-nuget.yml @@ -0,0 +1,33 @@ +# This workflow is triggered when a GitHub release is created. +# It can also be run manually to re-publish to NuGet in case it failed for some reason. +# You can run this workflow by navigating to https://www.github.com/imagekit-developer/imagekit-dotnet/actions/workflows/publish-nuget.yml +name: Publish to NuGet +on: + workflow_dispatch: + release: + types: [published] +jobs: + publish: + name: publish + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Set up .NET + uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 + with: + dotnet-version: '8.0.x' + + - name: Build and pack + run: dotnet pack + --configuration Release + --output "${{github.workspace}}/artifacts/packages" + + - name: Publish package to nuget.org + run: dotnet nuget push + $(find ${{ github.workspace }}/artifacts/packages/*.nupkg ! -name "*.symbols.nupkg") + --source https://api.nuget.org/v3/index.json + --api-key $NUGET_API_KEY + env: + NUGET_API_KEY: ${{ secrets.IMAGE_KIT_NUGET_API_KEY || secrets.NUGET_API_KEY }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index b3a7da81..d8b5a542 100644 --- a/.gitignore +++ b/.gitignore @@ -1,266 +1,10 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# User-specific files -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ - -# Visual Studio 2015 cache/options directory +.prism.log +.stdy.log +bin/ +obj/ .vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUNIT -*.VisualState.xml -TestResult.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# DNX -project.lock.json -project.fragment.lock.json -artifacts/ - -*_i.c -*_p.c -*_i.h -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# Code Coverage -*.dotCover -coverage.cobertura.xml -coverage.info -CoverageReport - - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings -# but database connection strings (with potential passwords) will be unencrypted -#*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/packages/* -# except build/, which is used as an MSBuild target. -!**/packages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config -# NuGet v3's project.json files produces more ignoreable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -node_modules/ -orleans.codegen.cs - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -*.mdf -*.ldf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# JetBrains Rider .idea/ -*.sln.iml - -# CodeRush -.cr/ -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc -.DS_Store \ No newline at end of file +# do not edit! excludes generated files used internally +.artifacts/ +devcontainer-lock.json \ No newline at end of file diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 00000000..112e32b3 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "6.0.0" +} \ No newline at end of file diff --git a/.stats.yml b/.stats.yml new file mode 100644 index 00000000..4355b752 --- /dev/null +++ b/.stats.yml @@ -0,0 +1,4 @@ +configured_endpoints: 48 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/imagekit-inc/imagekit-diversion-fecead543ea79ea907490f2625f2f06df319934a03458b182dce84c44d552dbd.yml +openapi_spec_hash: 964672a4901296fe54bf272bbdca0166 +config_hash: 66121ffadb78b9866f6b853f19b11f3d diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..921dd6d6 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + "recommendations": [ + "ms-dotnettools.csharp", + "editorconfig.editorconfig", + "github.vscode-github-actions", + "ms-dotnettools.vscode-dotnet-runtime", + "ms-dotnettools.csdevkit" + ], + "unwantedRecommendations": [] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index 180d9fb6..00000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,123 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "label": "build", - "command": "dotnet", - "type": "process", - "args": [ - "build", - "${workspaceFolder}/Imagekit.UnitTests/Imagekit.UnitTests.csproj", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "build", - "command": "dotnet", - "type": "shell", - "args": [ - "build", - "${workspaceRoot}/Imagekit/Imagekit.csproj", - "-f", - "netstandard2.1" - ], - "group": { - "kind": "build", - "isDefault": true - }, - "presentation": { - "panel": "shared", - "clear": true - }, - "problemMatcher": { - "base": "$msCompile", - "fileLocation": [ - "relative", - "${workspaceFolder}" - ] - } - }, - { - "label": "test", - "group": { - "kind": "test", - "isDefault": true - }, - "type": "process", - "command": "dotnet", - "args": [ - "test", - "${workspaceRoot}/Imagekit.UnitTests/Imagekit.UnitTests.csproj", - "--logger", - "html", - "/p:Configuration=Debug", - "/p:CollectCoverage=true", - "/p:CoverletOutputFormat=cobertura%2clcov", - "/p:Exclude=[*Tests]*" - ], - "presentation": { - "echo": true, - "reveal": "always", - "focus": true, - "panel": "shared", - "showReuseMessage": true, - "clear": true - } - }, - { - "label": "generate test coverage report", - "group": "none", - "command": "dotnet", - "type": "shell", - "args": [ - "reportgenerator", - "\"-reports:${workspaceRoot}/Imagekit.UnitTests/coverage.cobertura.xml\"", - "\"-targetdir:${workspaceRoot}/Imagekit.UnitTests/CoverageReport\"", - "-reportTypes:htmlInline" - ], - "options": { - "cwd": "${workspaceFolder}/Imagekit.UnitTests" - }, - "dependsOn": [ - "test" - ], - "presentation": { - "echo": true, - "reveal": "always", - "focus": true, - "panel": "shared", - "showReuseMessage": true, - "clear": false - }, - "problemMatcher": [] - }, - { - "label": "open test coverage report", - "group": "none", - "osx": { - "command": "open" - }, - "windows": { - "command": "start" - }, - "type": "shell", - "args": [ - "${workspaceRoot}/Imagekit.UnitTests/CoverageReport/index.htm" - ], - "problemMatcher": [], - "dependsOn": [ - "generate test coverage report" - ], - "presentation": { - "echo": true, - "reveal": "always", - "focus": true, - "panel": "shared", - "showReuseMessage": true, - "clear": true - } - } - ] -} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 25fd627e..eaacb5e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,74 +1,20 @@ # Changelog -All notable changes to this project will be documented in this file. -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 6.0.0 (2026-05-18) -## [5.0.0] +Full Changelog: [v0.0.1...v6.0.0](https://github.com/imagekit-developer/imagekit-dotnet/compare/v0.0.1...v6.0.0) -### Breaking changes +### Features -**Overlay syntax update** -* In version 5.0.0, we've removed the old overlay syntax parameters for transformations, such as `oi`, `ot`, `obg`, and [more](https://docs.imagekit.io/features/image-transformations/overlay). These parameters are deprecated and will start returning errors when used in URLs. Please migrate to the new layers syntax that supports overlay nesting, provides better positional control, and allows more transformations at the layer level. You can start with [examples](https://docs.imagekit.io/features/image-transformations/overlay-using-layers#examples) to learn quickly. -* You can migrate to the new layers syntax using the `raw` transformation parameter. +* **api:** manual updates ([c95f309](https://github.com/imagekit-developer/imagekit-dotnet/commit/c95f30943f1616f5b101e9ab1616332f386cf0c3)) +* **api:** manual updates ([02d0689](https://github.com/imagekit-developer/imagekit-dotnet/commit/02d0689fb2cd1536dd3f3f6df2d26c2c8eda8fba)) +* **docs:** enhance README with comprehensive usage examples and updated table of contents ([8b7dd50](https://github.com/imagekit-developer/imagekit-dotnet/commit/8b7dd50412c1977904293936409bc97e81965f0a)) +* **helper:** implement authentication parameters and URL building methods ([dcb863e](https://github.com/imagekit-developer/imagekit-dotnet/commit/dcb863edf27db1a4a28547dde8c4409398462b25)) +* **upload:** refactor upload URL handling and body serialization ([be1fe81](https://github.com/imagekit-developer/imagekit-dotnet/commit/be1fe810b62ff7949bdc53b6492f968694934b2f)) -### Added -- Parameters `EffectShadow` and `EffectGradient` for url generation. -- Upload options - - `transformation` for applying `pre` and `post` transformations. - - `checks` to perform server-side validations before file uploads. - - `isPublished` determines whether the file should be uploaded as published. -- Update parameter - - `publish` to configure the publication status of a file and its versions. -## [4.0.1] -### Fixed -- https://github.com/imagekit-developer/imagekit-dotnet/issues/50 +### Chores -## [4.0.0] -### Fixed -- https://github.com/imagekit-developer/imagekit-dotnet/issues/37 -- https://github.com/imagekit-developer/imagekit-dotnet/issues/41 -- https://github.com/imagekit-developer/imagekit-dotnet/issues/40 -- https://github.com/imagekit-developer/imagekit-dotnet/issues/38 -- https://github.com/imagekit-developer/imagekit-dotnet/issues/34 - -## [3.1.6] - 2021-07-21 -### Fixed -- Bug Fix - -## [3.1.5] - 2021-06-05 -### Changed -- Sort, searchQuery options added for ListAPI -- API reponse fields updated - -## [3.1.4] - 2021-04-12 -### Fixed -- Core package install bug fix - -## [3.1.3] - 2020-11-05 -### Fixed -- Delete Api bug fix - -## [3.1.2] - 2020-11-04 -### Fixed -- Upload Api fix - -## [3.1.1] - 2020-09-11 -### Changed -- Fix issue where tags were not set in some cases when uploading a file -- Allow more ways to set tags when updating file details - -## [3.1.0] - 2020-09-03 -### Added -- Async methods for all asynchronous calls -- `ClientImagekit` that supports client upload without the private key -- Support for .NET Standard 2.1 -- `ImagekitResponse.FileId` -- Some XML documentation - -### Changed -- The `Imagekit.Imagekit` class is deprecated; use `ServerImagekit` instead -- Fix issue where `isPrivateFile` was not included on upload -- Fix issue where type of `Gps.GPSLatitude`, `Gps.GPSLongitude`, and `GPSTimeStamp` was incorrect -- Some of the `ArgumentException`s are now `ArgumentNullException` and set the `ParamName` property of the exception. This is not a binary breaking change as `ArgumentNullException` inherits from `ArgumentException`, but if you are specifically looking for the `ArgumentException` type exactly, you may see a runtime issue, although this is very unlikely. +* remove custom code ([eb69aea](https://github.com/imagekit-developer/imagekit-dotnet/commit/eb69aeaa509fd4edefc0ef21cfe2e50fff8beb4a)) +* sync repo ([268016f](https://github.com/imagekit-developer/imagekit-dotnet/commit/268016fe2615138ca8814b559093c98a1e1d72de)) +* update SDK settings ([88259fb](https://github.com/imagekit-developer/imagekit-dotnet/commit/88259fbbef296c5e40584294c70fc68db21fad27)) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..c1a1bd76 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,37 @@ +## Setting up the environment + +To set up the repository, run: + +```sh +$ ./scripts/bootstrap +$ ./scripts/build +``` + +This will install required dependencies and build the SDK. + +## Modifying/Adding code + +Most of the SDK is generated code. Modifications to code will be persisted between generations, but may +result in merge conflicts between manual patches and changes from the generator. The generator will never +modify the contents of the `examples/` directory. + +## Using the repository from source + +To use a local version of this library from source in another project, add it using a directory reference: + +```sh +$ dotnet add reference /path/to/sdk/src/Imagekit +``` + +## Formatting and linting + +```sh +$ ./scripts/format +$ ./scripts/lint +``` + +## Running tests + +```sh +$ ./scripts/test +``` diff --git a/ImageKitSample/Program.cs b/ImageKitSample/Program.cs deleted file mode 100644 index 093bb84e..00000000 --- a/ImageKitSample/Program.cs +++ /dev/null @@ -1,482 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace ImagekitSample -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Net; - using Imagekit; - using Imagekit.Models; - using Imagekit.Models.Response; - using Imagekit.Sdk; - using Newtonsoft.Json.Linq; - using static Imagekit.Models.CustomMetaDataFieldSchemaObject; - - internal class Program - { - static void Main(string[] args) - { - - // Create Instance of ImageKit - ImagekitClient imagekit = new ImagekitClient("your_public_key", "your_private_key", "https://ik.imagekit.io/your_imagekit_id/endpoint"); - #region URL Generation - - - // Generating URLs - - string path = "/default_image.jpg"; - Transformation trans = new Transformation() - .Width(400) - .Height(300) - .AspectRatio("4-3") - .Quality(40) - .Crop("force").CropMode("extract"). - Focus("left"). - Format("jpeg"). - Background("A94D34"). - Border("5-A94D34"). - Rotation(90). - Blur(10). - Named("some_name"). - Progressive(true). - Lossless(true). - Trim(5). - Metadata(true). - ColorProfile(true). - DefaultImage("folder/file_name.jpg/"). //trailing slash case - Dpr(3). - EffectSharpen(10). - EffectUsm("2-2-0.8-0.024"). - EffectContrast(true). - EffectGray(). - EffectShadow(). - EffectGradient(). - Original(). - Raw("h-200,w-300,l-image,i-logo.png,l-end"); - - string imageUrl = imagekit.Url(trans).Path(path).TransformationPosition("query").Generate(); - - Console.WriteLine("Generated image URL - {0}", imageUrl); - - ///// Generating Signed URL - var imgUrl1 = "https://ik.imagekit.io/demo/default-image.jpg"; - string[] queryParams = { "b=query", "a=value" }; - try - { - var signedUrl = imagekit.Url(new Transformation().Width(400).Height(300)) - .Src(imgUrl1) - .QueryParameters(queryParams) - .ExpireSeconds(600) - .Signed() - .Generate(); - Console.WriteLine("Signed Url for first image transformed with height: 300, width: 400: - {0}", signedUrl); - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - } - #endregion - - #region Upload BY URI| Bytes | Base64 - - // Upload By URI - FileCreateRequest request = new FileCreateRequest - { - file = "http://www.google.com/images/logos/ps_logo2.png", - fileName = "file_name.jpg" - }; - Result resp1 = imagekit.Upload(request); - - // Upload by bytes - var webClient = new WebClient(); - byte[] bytes = webClient.DownloadData("http://www.google.com/images/logos/ps_logo2.png"); - - FileCreateRequest ob = new FileCreateRequest - { - file = bytes, - fileName = "file_name1.jpg" - }; - List tags = new List - { - "Software", - "Developer", - "Engineer" - }; - ob.tags = tags; - - string customCoordinates = "10,10,20,20"; - ob.customCoordinates = customCoordinates; - List responseFields = new List - { - "isPrivateFile", - "tags", - "customCoordinates" - }; - ob.responseFields = responseFields; - List ext = new List(); - BackGroundImage bck1 = new BackGroundImage - { - name = "remove-bg", - options = new options() - { add_shadow = true, semitransparency = false, bg_image_url = "http://www.google.com/images/logos/ps_logo2.png" } - }; - AutoTags autoTags = new AutoTags - { - name = "google-auto-tagging", - maxTags = 5, - minConfidence = 95 - }; - TransformationObject transformationObject = new TransformationObject - { - type = "transformation", - value = "w-100" - }; - List postTransformations = new List(); - postTransformations.Add(transformationObject); - UploadTransformation uploadTransformation = new UploadTransformation - { - pre = "l-text,i-Imagekit,fs-50,l-end", - post = postTransformations, - }; - ext.Add(bck1); - ext.Add(autoTags); - ob.extensions = ext; - ob.webhookUrl = "https://webhook.site/c78d617f_33bc_40d9_9e61_608999721e2e"; - ob.useUniqueFileName = true; - ob.folder = "dummy_folder"; - ob.isPrivateFile = false; - ob.overwriteFile = true; - ob.overwriteAITags = true; - ob.overwriteTags = true; - ob.overwriteCustomMetadata = true; - ob.transformation= uploadTransformation; - Result resp2 = imagekit.Upload(ob); - - // Get Base64 - byte[] bytes1 = webClient.DownloadData("http://www.google.com/images/logos/ps_logo2.png"); - - byte[] imageArray = bytes1; - string base64ImageRepresentation = Convert.ToBase64String(imageArray); - - // Upload by Base64 - FileCreateRequest ob2 = new FileCreateRequest - { - file = base64ImageRepresentation, - fileName = Guid.NewGuid().ToString() - }; - Result resp = imagekit.Upload(ob2); - #endregion - - #region UpdateFile - - //Update File Request - FileUpdateRequest updateob = new FileUpdateRequest - { - fileId = "fileId", - }; - List updatetags = new List - { - "Software", - "Developer", - "Engineer" - }; - updateob.tags = updatetags; - - string updatecustomCoordinates = "10,10,20,20"; - updateob.customCoordinates = updatecustomCoordinates; - List updateresponseFields = new List - { - "isPrivateFile", - "tags", - "customCoordinates" - }; - - List extModel = new List(); - BackGroundImage bck = new BackGroundImage - { - name = "remove-bg", - options = new options() { add_shadow = true, semitransparency = false, bg_color = "green" } - }; - extModel.Add(bck); - updateob.extensions = extModel; - updateob.webhookUrl = "https://webhook.site/c78d617f_33bc_40d9_9e61_608999721e2e"; - - Result updateresp = imagekit.UpdateFileDetail(updateob); - - #endregion - - #region File Management - - // List and search files - GetFileListRequest model = new GetFileListRequest - { - Name = "file_name.jpg", - Type = "file", - Limit = 10, - Skip = 0, - Sort = "ASC_CREATED", - SearchQuery = "createdAt >= \"7d\"", - FileType = "image", - Tags = new string[] { "sale", "summer" }, - Path = "/" - }; - ResultList res = imagekit.GetFileListRequest(model); - - // Get File Details - Result res1 = imagekit.GetFileDetail("file_Id"); - - // Delete File by FileId - ResultDelete res2 = imagekit.DeleteFile("file_Id"); - - // Bulk Delete - List ob3 = new List(); - ob3.Add("fileId_1"); - ob3.Add("fileId_2"); - ResultFileDelete resultFileDelete = imagekit.BulkDeleteFiles(ob3); - - // Copy File - CopyFileRequest cpyRequest = new CopyFileRequest - { - sourceFilePath = "path_1", - destinationPath = "path_2" - }; - ResultNoContent resultNoContent = imagekit.CopyFile(cpyRequest); - - // MoveFile - MoveFileRequest moveFile = new MoveFileRequest - { - sourceFilePath = "path_1", - destinationPath = "path_2" - }; - ResultNoContent resultNoContentMoveFile = imagekit.MoveFile(moveFile); - - // RenameFile - RenameFileRequest renameFileRequest = new RenameFileRequest - { - filePath = "path_1", - newFileName = "file_name", - purgeCache = false - }; - ResultRenameFile resultRenameFile = imagekit.RenameFile(renameFileRequest); - - #endregion - - #region Tags - TagsRequest tagsRequest = new TagsRequest - { - tags = new List - { - "tag_1", - "tag_2" - }, - fileIds = new List - { - "fileId_1", - }, - }; - ResultTags resultTags = imagekit.AddTags(tagsRequest); - - TagsRequest removeTagsRequest = new TagsRequest - { - tags = new List - { - "tag_1", - "tag_2" - }, - fileIds = new List - { - "fileId_1", - }, - }; - ResultTags removeTags = imagekit.RemoveTags(removeTagsRequest); - - AITagsRequest removeAITagsRequest = new AITagsRequest - { - AITags = new List - { - "tag_1", - "tag_2" - }, - fileIds = new List - { - "fileId_1", - }, - }; - ResultTags removeAITags = imagekit.RemoveAITags(removeAITagsRequest); - - #endregion - - #region FileVersionRequest - DeleteFileVersionRequest delRequest = new DeleteFileVersionRequest - { - fileId = "file_Id", - versionId = "version_Id" - }; - ResultNoContent resultNoContent1 = imagekit.DeleteFileVersion(delRequest); - - // RestoreFileVersion - Result result = imagekit.RestoreFileVersion("file_Id", "version_Id"); - - // GetFileVersions - ResultFileVersions resultFileVersions = imagekit.GetFileVersions("file_Id"); - - // GetFileVersionDetails - ResultFileVersionDetails resultFileVersionDetails = imagekit.GetFileVersionDetails("file_Id", "version_Id"); - - #endregion - - #region ManageFolder - - CreateFolderRequest createFolderRequest = new CreateFolderRequest - { - folderName = "folder_name", - parentFolderPath = "source/folder/path" - }; - ResultEmptyBlock resultEmptyBlock = imagekit.CreateFolder(createFolderRequest); - - // DeleteFolderRequest - DeleteFolderRequest deleteFolderRequest = new DeleteFolderRequest - { - folderPath = "source/folder/path/folder_name", - }; - ResultNoContent resultNoContent2 = imagekit.DeleteFolder(deleteFolderRequest); - - // CopyFolder - CopyFolderRequest cpyFolderRequest = new CopyFolderRequest - { - sourceFolderPath = "path_1", - destinationPath = "path_2", - includeFileVersions = true - }; - - ResultOfFolderActions resultOfFolderActions = imagekit.CopyFolder(cpyFolderRequest); - - // MoveFolder - MoveFolderRequest moveFolderRequest = new MoveFolderRequest - { - sourceFolderPath = "path_1", - destinationPath = "path_2" - }; - - ResultOfFolderActions resultOfFolderActions1 = imagekit.MoveFolder(moveFolderRequest); - - #endregion - - #region GetBulkJobStatus - ResultBulkJobStatus resultBulkJobStatus = imagekit.GetBulkJobStatus("job_Id"); - - #endregion - - #region Purge - - ResultCache resultCache = imagekit.PurgeCache("url"); - - ResultCacheStatus resultCacheStatus = imagekit.PurgeStatus("request_Id"); - - #endregion - - #region Metadata - ResultMetaData resultMetaData = imagekit.GetFileMetadata("file_Id"); - - ResultMetaData resultMetaData1 = imagekit.GetRemoteFileMetadata("https://ik.imagekit.io/demo/medium_cafe_B1iTdD0C.jpg"); - - // CustomMetaDataFields - ResultCustomMetaDataFieldList resultCustomMetaDataFieldList = imagekit.GetCustomMetaDataFields(true); - - // CreateCustomMetaDataFields - CustomMetaDataFieldCreateRequest requestModelDate = new CustomMetaDataFieldCreateRequest - { - name = "custom_meta_Date", - label = "TestmetaDat" - }; - CustomMetaDataFieldSchemaObject schemaDate = new CustomMetaDataFieldSchemaObject - { - type = CustomMetaDataTypeEnum.Date.ToString(), - minValue = "2022-11-30T10:11:10+00:00", - maxValue = "2022-12-30T10:11:10+00:00", - isValueRequired = true, - defaultValue = "2022-12-30T10:11:10+00:00" - }; - - requestModelDate.schema = schemaDate; - ResultCustomMetaDataField resultCustomMetaDataFieldDate = imagekit.CreateCustomMetaDataFields(requestModelDate); - - - CustomMetaDataFieldCreateRequest requestModel = new CustomMetaDataFieldCreateRequest - { - name = "custom_meta_1", - label = "Testmeta" - }; - CustomMetaDataFieldSchemaObject schema = new CustomMetaDataFieldSchemaObject - { - type = CustomMetaDataTypeEnum.Number.ToString(), - minValue = 2000, - maxValue = 3000, - isValueRequired = true, - defaultValue = 2500 - }; - - requestModel.schema = schema; - ResultCustomMetaDataField resultCustomMetaDataField1 = imagekit.CreateCustomMetaDataFields(requestModel); - - CustomMetaDataFieldCreateRequest requestModel1 = new CustomMetaDataFieldCreateRequest - { - name = "custom_meta_2", - label = "Testmeta" - }; - CustomMetaDataFieldSchemaObject schema1 = new CustomMetaDataFieldSchemaObject - { - type = CustomMetaDataTypeEnum.Text.ToString(), - minLength = 1000, - maxLength = 2000, - isValueRequired = true, - defaultValue = "2500" - }; - - requestModel1.schema = schema1; - ResultCustomMetaDataField resultCustomMetaDataField = imagekit.CreateCustomMetaDataFields(requestModel1); - - CustomMetaDataFieldCreateRequest requestModelSelect = new CustomMetaDataFieldCreateRequest - { - name = "custom_meta_Select1", - label = "TestmetaSelect1" - }; - CustomMetaDataFieldSchemaObject schemaSelect = new CustomMetaDataFieldSchemaObject - { - type = CustomMetaDataTypeEnum.SingleSelect.ToString(), - selectOptions = new string[] { "small", "medium", "large" }, - isValueRequired = true, - defaultValue = "medium" - }; - requestModelSelect.schema = schemaSelect; - ResultCustomMetaDataField resultCustomMetaDataFieldSelect = imagekit.CreateCustomMetaDataFields(requestModelSelect); - - - // UpdateCustomMetaDataFields - CustomMetaDataFieldUpdateRequest requestUpdateModel = new CustomMetaDataFieldUpdateRequest - { - Id = "field_Id", - }; - CustomMetaDataFieldSchemaObject updateschema = new CustomMetaDataFieldSchemaObject - { - type = "Number", - minValue = 300, - maxValue = 500 - }; - - requestUpdateModel.schema = updateschema; - ResultCustomMetaDataField resultCustomMetaDataFieldUpdate = imagekit.UpdateCustomMetaDataFields(requestUpdateModel); - - //Delete Custom MetaData - ResultNoContent resultNoContentDel = imagekit.DeleteCustomMetaDataField("field_id"); - - // Get Authentication Token - var authenticationParameters = imagekit.GetAuthenticationParameters("your_token"); - Console.WriteLine("Authentication Parameters: {0}", JToken.FromObject(authenticationParameters).ToString()); - #endregion - } - } -} diff --git a/ImageKitSample/test.jpg b/ImageKitSample/test.jpg deleted file mode 100644 index 79de088c..00000000 Binary files a/ImageKitSample/test.jpg and /dev/null differ diff --git a/Imagekit.UnitTests/FileVersion/FileVersionTestAsync.cs b/Imagekit.UnitTests/FileVersion/FileVersionTestAsync.cs deleted file mode 100644 index 339de562..00000000 --- a/Imagekit.UnitTests/FileVersion/FileVersionTestAsync.cs +++ /dev/null @@ -1,481 +0,0 @@ -using Imagekit.Constant; -using Imagekit.Models; -using Newtonsoft.Json; -using System; -using System.Net; -using System.Net.Http; -using Xunit; - -namespace Imagekit.UnitTests.FileVersion -{ - - public class FileVersionTestAsync - - { - private const string GOOD_PUBLICKEY = "abc"; - private const string GOOD_URLENDPOINT = "https://dasdsad.dad.io/"; - - - - - - [Fact] - public void Missing_Object_FileVersionException() - { - DeleteFileVersionRequest model = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.DeleteFileVersionAsync(model)); - Assert.Equal(ErrorMessages.InvalidDelVerValue, ex.Result.Message); - } - [Fact] - public void Missing_fileId_FileVersionException() - { - DeleteFileVersionRequest model = new DeleteFileVersionRequest - { - versionId = "abc" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.DeleteFileVersionAsync(model)); - Assert.Equal(ErrorMessages.InvalidFieldIdDelVerValue, ex.Result.Message); - } - - [Fact] - public void Missing_versionId_FileVersionException() - { - DeleteFileVersionRequest model = new DeleteFileVersionRequest - { - versionId = "", - fileId = "sas" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.DeleteFileVersionAsync(model)); - Assert.Equal(ErrorMessages.InvalidversionIdDelVerValue, ex.Result.Message); - } - - [Fact] - public void CopyFile_Default() - { - CopyFileRequest model = new CopyFileRequest - { - sourceFilePath = "Tst3", - destinationPath = "Tst3", - includeFileVersions = true - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.CopyFileAsync(model).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - - [Fact] - public void Missing_Obj_CopyFileException() - { - CopyFileRequest model = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.CopyFileAsync(model)); - Assert.Equal(ErrorMessages.InvalidCopyValue, ex.Result.Message); - } - - - - [Fact] - public void Missing_Source_CopyFileException() - { - CopyFileRequest model = new CopyFileRequest - { - destinationPath = "abc" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.CopyFileAsync(model)); - Assert.Equal(ErrorMessages.InvalidSourceValue, ex.Result.Message); - } - - [Fact] - public void Missing_Destination_CopyFileException() - { - CopyFileRequest model = new CopyFileRequest - { - sourceFilePath = "abc" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.CopyFileAsync(model)); - Assert.Equal(ErrorMessages.InvalidDestinationValue, ex.Result.Message); - } - - [Fact] - public void MoveFile_Default() - { - MoveFileRequest model = new MoveFileRequest - { - sourceFilePath = "Tst3", - destinationPath = "Tst3" - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.MoveFileAsync(model).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - - [Fact] - public void Missing_Obj_MoveFileException() - { - MoveFileRequest model = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.MoveFileAsync(model)); - Assert.Equal(ErrorMessages.InvalidCopyValue, ex.Result.Message); - } - - [Fact] - public void Missing_Source_MoveFileException() - { - MoveFileRequest model = new MoveFileRequest - { - destinationPath = "abc" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.MoveFileAsync(model)); - Assert.Equal(ErrorMessages.InvalidSourceValue, ex.Result.Message); - } - - [Fact] - public void Missing_Destination_MoveFileException() - { - MoveFileRequest model = new MoveFileRequest - { - sourceFilePath = "abc" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.MoveFileAsync(model)); - Assert.Equal(ErrorMessages.InvalidDestinationValue, ex.Result.Message); - } - - [Fact] - public void RenameFile_Default() - { - RenameFileRequest model = new RenameFileRequest - { - filePath = "Tst3", - newFileName = "Tst4", - purgeCache = false - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.RenameFileAsync(model).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - - [Fact] - public void Missing_FilePath_RenameFileException() - { - RenameFileRequest model = new RenameFileRequest - { - newFileName = "test" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.RenameFileAsync(model)); - Assert.Equal(ErrorMessages.InvalidRenameFilePathValue, ex.Result.Message); - } - [Fact] - public void Missing_NewFileName_RenameFileException() - { - RenameFileRequest model = new RenameFileRequest - { - filePath = "test" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.RenameFileAsync(model)); - Assert.Equal(ErrorMessages.InvalidRenameNewFileNameValue, ex.Result.Message); - } - - - - - [Fact] - public void GetFileVersions_Default() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.GetFileVersionsAsync("abc").Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - - - [Fact] - public void MISSING_FILE_ID_FileVersionsException() - { - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.GetFileVersionsAsync("")); - Assert.Equal(ErrorMessages.FileIdMissing, ex.Result.Message); - } - [Fact] - public void RestoreFileVersion_Default() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (Result)restClient.RestoreFileVersionAsync("abc", "1").Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - - [Fact] - public void Missing_Restore_File_Exception() - { - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.RestoreFileVersionAsync("", "123")); - Assert.Equal(ErrorMessages.InvalidFieldIdDelVerValue, ex.Result.Message); - } - [Fact] - public void Missing_Restore_Version_Exception() - { - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.RestoreFileVersionAsync("123", "")); - Assert.Equal(ErrorMessages.InvalidversionIdDelVerValue, ex.Result.Message); - } - - [Fact] - public void Missing_Restore_File_Version_Exception() - { - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.RestoreFileVersionAsync("", "")); - Assert.Equal(ErrorMessages.InvalidDelVerValue, ex.Result.Message); - } - [Fact] - public void GetFileVersionDetails_Default() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.GetFileVersionDetailsAsync("abc", "1").Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void Missing_FileVersionDetailsException() - { - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.GetFileVersionDetailsAsync("", "")); - Assert.Equal(ErrorMessages.InvalidDelVerValue, ex.Result.Message); - } - [Fact] - public void Missing_FileDetails_Exception() - { - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.RestoreFileVersionAsync("", "123")); - Assert.Equal(ErrorMessages.InvalidFieldIdDelVerValue, ex.Result.Message); - } - [Fact] - public void Missing_VersionDetails_Exception() - { - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.RestoreFileVersionAsync("123", "")); - Assert.Equal(ErrorMessages.InvalidversionIdDelVerValue, ex.Result.Message); - } - } -} - - - - diff --git a/Imagekit.UnitTests/FileVersion/FileVersionTestNonAsync.cs b/Imagekit.UnitTests/FileVersion/FileVersionTestNonAsync.cs deleted file mode 100644 index 2e90db7c..00000000 --- a/Imagekit.UnitTests/FileVersion/FileVersionTestNonAsync.cs +++ /dev/null @@ -1,482 +0,0 @@ -using Imagekit.Constant; -using Imagekit.Models; -using Newtonsoft.Json; -using System; -using System.Net; -using System.Net.Http; -using Xunit; - -namespace Imagekit.UnitTests.FileVersion -{ - - public class FileVersionTestNonAsync - - { - private const string GOOD_PUBLICKEY = "abc"; - private const string GOOD_URLENDPOINT = "https://dasdsad.dad.io/"; - - - [Fact] - public void Missing_Object_FileVersionException_NonAsync() - { - DeleteFileVersionRequest model = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.DeleteFileVersion(model)); - Assert.Equal(ErrorMessages.InvalidDelVerValue, ex.Message); - } - [Fact] - public void Missing_fileId_FileVersionException_NonAsync() - { - DeleteFileVersionRequest model = new DeleteFileVersionRequest - { - versionId = "abc" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.DeleteFileVersion(model)); - Assert.Equal(ErrorMessages.InvalidFieldIdDelVerValue, ex.Message); - } - - [Fact] - public void Missing_versionId_FileVersionException_NonAsync() - { - DeleteFileVersionRequest model = new DeleteFileVersionRequest - { - versionId = "", - fileId = "sas" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.DeleteFileVersion(model)); - Assert.Equal(ErrorMessages.InvalidversionIdDelVerValue, ex.Message); - } - - [Fact] - public void CopyFile_Default_NonAsync() - { - CopyFileRequest model = new CopyFileRequest - { - sourceFilePath = "Tst3", - destinationPath = "Tst3", - includeFileVersions = true - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.CopyFile(model); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - - [Fact] - public void Missing_Obj_CopyFileException_NonAsync() - { - CopyFileRequest model = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.CopyFile(model)); - Assert.Equal(ErrorMessages.InvalidCopyValue, ex.Message); - } - - - - [Fact] - public void Missing_Source_CopyFileException_NonAsync() - { - CopyFileRequest model = new CopyFileRequest - { - destinationPath = "abc" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.CopyFile(model)); - Assert.Equal(ErrorMessages.InvalidSourceValue, ex.Message); - } - - [Fact] - public void Missing_Destination_CopyFileException_NonAsync() - { - CopyFileRequest model = new CopyFileRequest - { - sourceFilePath = "abc" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.CopyFile(model)); - Assert.Equal(ErrorMessages.InvalidDestinationValue, ex.Message); - } - - [Fact] - public void MoveFile_Default_NonAsync() - { - MoveFileRequest model = new MoveFileRequest - { - sourceFilePath = "Tst3", - destinationPath = "Tst3" - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.MoveFile(model); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - - [Fact] - public void Missing_Obj_MoveFileException_NonAsync() - { - MoveFileRequest model = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.MoveFile(model)); - Assert.Equal(ErrorMessages.InvalidCopyValue, ex.Message); - } - - [Fact] - public void Missing_Source_MoveFileException_NonAsync() - { - MoveFileRequest model = new MoveFileRequest - { - destinationPath = "abc" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.MoveFile(model)); - Assert.Equal(ErrorMessages.InvalidSourceValue, ex.Message); - } - - [Fact] - public void Missing_Destination_MoveFileException_NonAsync() - { - MoveFileRequest model = new MoveFileRequest - { - sourceFilePath = "abc" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.MoveFile(model)); - Assert.Equal(ErrorMessages.InvalidDestinationValue, ex.Message); - } - - [Fact] - public void RenameFile_Default_NonAsync() - { - RenameFileRequest model = new RenameFileRequest - { - filePath = "Tst3", - newFileName = "Tst4", - purgeCache = false - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.RenameFile(model); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - - [Fact] - public void Missing_FilePath_RenameFileException_NonAsync() - { - RenameFileRequest model = new RenameFileRequest - { - newFileName = "test" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.RenameFile(model)); - Assert.Equal(ErrorMessages.InvalidRenameFilePathValue, ex.Message); - } - [Fact] - public void Missing_NewFileName_RenameFileException_NonAsync() - { - RenameFileRequest model = new RenameFileRequest - { - filePath = "test" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.RenameFile(model)); - Assert.Equal(ErrorMessages.InvalidRenameNewFileNameValue, ex.Message); - } - - - - - [Fact] - public void GetFileVersions_Default_NonAsync() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.GetFileVersions("abc"); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - - - [Fact] - public void MISSING_FILE_ID_FileVersionsException_NonAsync() - { - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.GetFileVersions("")); - Assert.Equal(ErrorMessages.FileIdMissing, ex.Message); - } - [Fact] - public void RestoreFileVersion_Default_NonAsync() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (Result)restClient.RestoreFileVersion("abc", "1"); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - - [Fact] - public void Missing_Restore_File_Exception_NonAsync() - { - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.RestoreFileVersion("", "123")); - Assert.Equal(ErrorMessages.InvalidFieldIdDelVerValue, ex.Message); - } - [Fact] - public void Missing_Restore_Version_Exception_NonAsync() - { - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.RestoreFileVersion("123", "")); - Assert.Equal(ErrorMessages.InvalidversionIdDelVerValue, ex.Message); - } - - [Fact] - public void Missing_Restore_File_Version_Exception_NonAsync() - { - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.RestoreFileVersion("", "")); - Assert.Equal(ErrorMessages.InvalidDelVerValue, ex.Message); - } - [Fact] - public void GetFileVersionDetails_Default_NonAsync() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.GetFileVersionDetails("abc", "1"); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void Missing_FileVersionDetailsException_NonAsync() - { - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.GetFileVersionDetails("", "")); - Assert.Equal(ErrorMessages.InvalidDelVerValue, ex.Message); - } - [Fact] - public void Missing_FileDetails_Exception_NonAsync() - { - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.RestoreFileVersion("", "123")); - Assert.Equal(ErrorMessages.InvalidFieldIdDelVerValue, ex.Message); - } - [Fact] - public void Missing_VersionDetails_Exception_NonAsync() - { - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.RestoreFileVersion("123", "")); - Assert.Equal(ErrorMessages.InvalidversionIdDelVerValue, ex.Message); - } - - - - - } -} - - - - diff --git a/Imagekit.UnitTests/ImageKitRequestModelValidation.cs b/Imagekit.UnitTests/ImageKitRequestModelValidation.cs deleted file mode 100644 index 67028bcf..00000000 --- a/Imagekit.UnitTests/ImageKitRequestModelValidation.cs +++ /dev/null @@ -1,850 +0,0 @@ -using Imagekit.Constant; -using Imagekit.Models; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Net.Http; -using Xunit; -using RichardSzalay.MockHttp; -using Imagekit.Helper; -using static Imagekit.Models.CustomMetaDataFieldSchemaObject; - -namespace Imagekit.UnitTests -{ - public class ImageKitRequestModelValidation - { - - private const string GOOD_PRIVATEKEY = "private_key"; - private const string GOOD_URLENDPOINT = "https://endpoint_url.io/"; - - [Fact] - public void UploadFileRequest_ModelValidation() - { - string json = "--ImageKit-dLV9Wyq26L\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Disposition: form-data; name=fileName\r\n\r\ntest.jpg\r\n--ImageKit-dLV9Wyq26L\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Disposition: form-data; name=file\r\n\r\nhttps://homepages.cae.wisc.edu/~ece533/images/cat.png\r\n--ImageKit-dLV9Wyq26L\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Disposition: form-data; name=useUniqueFileName\r\n\r\ntrue\r\n--ImageKit-dLV9Wyq26L\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Disposition: form-data; name=tags\r\n\r\nSoftware,Developer,Engineer\r\n--ImageKit-dLV9Wyq26L\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Disposition: form-data; name=folder\r\n\r\ndummy-folder\r\n--ImageKit-dLV9Wyq26L\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Disposition: form-data; name=customCoordinates\r\n\r\n10,10,20,20\r\n--ImageKit-dLV9Wyq26L\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Disposition: form-data; name=overwriteFile\r\n\r\ntrue\r\n--ImageKit-dLV9Wyq26L\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Disposition: form-data; name=overwriteAITags\r\n\r\ntrue\r\n--ImageKit-dLV9Wyq26L\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Disposition: form-data; name=overwriteTags\r\n\r\ntrue\r\n--ImageKit-dLV9Wyq26L\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Disposition: form-data; name=overwriteCustomMetadata\r\n\r\nfalse\r\n--ImageKit-dLV9Wyq26L\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Disposition: form-data; name=extensions\r\n\r\n[{\"options\":{\"add_shadow\":true,\"semitransparency\":false,\"bg_image_url\":\"http://www.google.com/images/logos/ps_logo2.png\"},\"name\":\"remove-bg\"},{\"minConfidence\":95,\"maxTags\":5,\"name\":\"google-auto-tagging\"}]\r\n--ImageKit-dLV9Wyq26L\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Disposition: form-data; name=webhookUrl\r\n\r\nhttps://webhook.site/c78d617f-33bc-40d9-9e61-608999721e2e\r\n--ImageKit-dLV9Wyq26L\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Disposition: form-data; name=customMetadata\r\n\r\n{\"price\":2000}\r\n--ImageKit-dLV9Wyq26L--\r\n"; - string url = "https://upload.imagekit.io/api/v1/files/upload"; - FileCreateRequest ob = new FileCreateRequest - { - file = "https://homepages.cae.wisc.edu/~ece533/images/cat.png", - fileName = "test.jpg", - }; - List tags = new List - { - "Software", - "Developer", - "Engineer" - }; - ob.tags = tags; - - string customCoordinates = "10,10,20,20"; - ob.customCoordinates = customCoordinates; - List model1 = new List(); - BackGroundImage bck = new BackGroundImage - { - name = "remove-bg", - options = new options() { add_shadow = true, semitransparency = false, bg_image_url = "http://www.google.com/images/logos/ps_logo2.png" } - }; - AutoTags autoTags = new AutoTags - { - name = "google-auto-tagging", - maxTags = 5, - minConfidence = 95 - }; - model1.Add(bck); - model1.Add(autoTags); - ob.extensions = model1; - ob.webhookUrl = "https://webhook.site/c78d617f-33bc-40d9-9e61-608999721e2e"; - Hashtable model = new Hashtable - { - { "price", 2000 } - }; - ob.customMetadata = model; - ob.useUniqueFileName = true; - ob.folder = "dummy-folder"; - ob.isPrivateFile = false; - ob.overwriteFile = true; - ob.overwriteAITags = true; - ob.overwriteTags = true; - ob.overwriteCustomMetadata = true; - List responseFields = new List - { - "isPrivateFile", - "tags", - "customCoordinates" - }; - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Post)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .WithContent(json) - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.Upload(ob); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void UpdateFileDetail_ModelValidation() - { - string url = string.Format("https://api.imagekit.io/v1/files/{0}/details", "file-Id"); - FileUpdateRequest ob = new FileUpdateRequest - { - fileId = "file-Id", - - }; - List tags = new List - { - "Software", - "Developer", - "Engineer" - }; - ob.tags = tags; - - string customCoordinates = "10,10,20,20"; - ob.customCoordinates = customCoordinates; - List modelExt = new List(); - BackGroundImage bck = new BackGroundImage - { - name = "remove-bg", - options = new options() { add_shadow = true, semitransparency = false, bg_color = "green", bg_image_url = "http://www.google.com/images/logos/ps_logo2.png" } - }; - AutoTags autoTags = new AutoTags - { - name = "google-auto-tagging", - maxTags = 5, - minConfidence = 95 - }; - modelExt.Add(autoTags); - modelExt.Add(bck); - ob.extensions = modelExt; - ob.webhookUrl = "https://webhook.site/c78d617f-33bc-40d9-9e61-608999721e2e"; - Hashtable model = new Hashtable - { - { "price", 2000 } - }; - ob.customMetadata = model; - string result = "{\"fileId\":\"file-Id\",\"webhookUrl\":\"https://webhook.site/c78d617f-33bc-40d9-9e61-608999721e2e\",\"extensions\":[{\"minConfidence\":95,\"maxTags\":5,\"name\":\"google-auto-tagging\"},{\"options\":{\"add_shadow\":true,\"semitransparency\":false,\"bg_color\":\"green\",\"bg_image_url\":\"http://www.google.com/images/logos/ps_logo2.png\"},\"name\":\"remove-bg\"}],\"tags\":[\"Software\",\"Developer\",\"Engineer\"],\"customCoordinates\":\"10,10,20,20\",\"customMetadata\":{\"price\":2000}}"; - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .WithContent(result) - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.UpdateFileDetail(ob); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void GetFileListRequest_ModelValidation() - { - GetFileListRequest ob = new GetFileListRequest - { - Name = "Test", - Limit = 10, - Skip = 20, - Type = "Test", - Path = "Test", - Sort = "Test", - SearchQuery = "Test", - FileType = "Test", - Tags = null - }; - string param = "sort=Test&path=Test&searchQuery=Test&fileType=Test&name=Test&limit=10&skip=20"; - string url = string.Format("https://api.imagekit.io/v1/files/?{0}", param); - - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Get)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.GetFileListRequest(ob); - mockHttp.VerifyNoOutstandingExpectation(); - } - [Fact] - public void PurgeCache_ModelValidation() - { - string url = string.Format("https://api.imagekit.io/v1/files/purge"); - string reqJosn = "{\"url\":\"path\"}"; - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Post)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .WithContent(reqJosn) - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.PurgeCache("path"); - mockHttp.VerifyNoOutstandingExpectation(); - } - [Fact] - public void DeleteFileRequest_ModelValidation() - { - string url = string.Format("https://api.imagekit.io/v1/files/{0}/", "fileId"); - - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Delete)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.DeleteFile("fileId"); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void PurgeStatusRequest_ModelValidation() - { - string url = string.Format("https://api.imagekit.io/v1/files/purge/{0}", "purgeRequestId"); - - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Get)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.PurgeStatus("purgeRequestId"); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void GetFileDetailRequest_ModelValidation() - { - string url = string.Format("https://api.imagekit.io/v1/files/{0}/details", "fileId"); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Get)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.GetFileDetail("fileId"); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void BulkDeleteFilesRequest_ModelValidation() - { - string reqJosn = "{\"fileIds\":[\"fileId1\",\"fileId2\"]}"; - - List fileIds = new List - { - "fileId1", - "fileId2" - }; - string url = string.Format("https://api.imagekit.io/v1/files/batch/deleteByfileIds"); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Post)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .WithContent(reqJosn) - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.BulkDeleteFiles(fileIds); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void GetFileMetaDataRequest_ModelValidation() - { - string url = string.Format("https://api.imagekit.io/v1/files/{0}/metadata", "fileId"); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Get)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.GetFileMetaData("fileId"); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void GetRemoteFileMetaDataRequest_ModelValidation() - { - string url = string.Format("https://api.imagekit.io/v1/metadata?url={0}", "url"); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Get)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.GetRemoteFileMetaData("url"); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void AddTagsRequest_ModelValidation() - { - string reqJosn = "{\"fileIds\":[\"abc\"],\"tags\":[\"abc\",\"abc\"]}"; - - TagsRequest tagsRequest = new TagsRequest - { - tags = new List - { - "abc", - "abc" - }, - fileIds = new List - { - "abc" - } - }; - string url = "https://api.imagekit.io/v1/files/addTags"; - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Post)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .WithContent(reqJosn) - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.ManageTags(tagsRequest, "addTags"); - mockHttp.VerifyNoOutstandingExpectation(); - } - [Fact] - public void RemoveTagsRequest_ModelValidation() - { - string reqJosn = "{\"fileIds\":[\"abc\"],\"tags\":[\"abc\",\"abc\"]}"; - - TagsRequest tagsRequest = new TagsRequest - { - tags = new List - { - "abc", - "abc" - }, - fileIds = new List - { - "abc" - } - }; - string url = string.Format("https://api.imagekit.io/" + UrlHandler.RemoveTags); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Post)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .WithContent(reqJosn) - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.ManageTags(tagsRequest, "removeTags"); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void RemoveAITagsRequest_ModelValidation() - { - string reqJosn = "{\"fileIds\":[\"abc\"],\"AITags\":[\"abc\",\"abc\"]}"; - - AITagsRequest tagsRequest = new AITagsRequest - { - AITags = new List - { - "abc", - "abc" - }, - fileIds = new List - { - "abc" - } - }; - string url = string.Format("https://api.imagekit.io/" + UrlHandler.RemoveAITags); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Post)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .WithContent(reqJosn) - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.RemoveAITags(tagsRequest); - mockHttp.VerifyNoOutstandingExpectation(); - } - [Fact] - public void GetCustomMetaDataFields_ModelValidation() - { - string url = string.Format("https://api.imagekit.io/v1/customMetadataFields?includeDeleted={0}", true); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Get)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.GetCustomMetaDataFields(true); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void GetCustomMetaDataFieldsEmpty_ModelValidation() - { - string url = string.Format("https://api.imagekit.io/v1/customMetadataFields?includeDeleted={0}", false); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Get)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.GetCustomMetaDataFields(); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void GetCustomMetaDataFieldsFalse_ModelValidation() - { - string url = string.Format("https://api.imagekit.io/v1/customMetadataFields?includeDeleted={0}", false); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Get)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.GetCustomMetaDataFields(false); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void CreateCustomMetaDataFields_ModelValidation() - { - string reqJosn = "{\n \"name\": \"Tst3\",\n \"label\": \"Test3\",\n \"schema\": {\n \"type\": \"Number\",\n \"minValue\": 1000,\n \"maxValue\": 3000,\n \"isValueRequired\": true,\n \"defaultValue\": 2500\n }\n}"; - CustomMetaDataFieldCreateRequest model = new CustomMetaDataFieldCreateRequest - { - name = "Tst3", - label = "Test3" - }; - CustomMetaDataFieldSchemaObject schema = new CustomMetaDataFieldSchemaObject - { - type = "Number", - minValue = 1000, - maxValue = 3000, - isValueRequired = true, - defaultValue = 2500 - - }; - model.schema = schema; - string url = "https://api.imagekit.io/v1/customMetadataFields"; - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Post)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .WithContent(reqJosn) - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.CreateCustomMetaDataFields(model); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void CreateCustomMetaDataFields2_ModelValidation() - { - string reqJosn = "{\n \"name\": \"Tst3\",\n \"label\": \"Test3\",\n \"schema\": {\n \"type\": \"Number\",\n \"minValue\": 1000,\n \"maxValue\": 3000,\n \"isValueRequired\": false\n }\n}"; - CustomMetaDataFieldCreateRequest model = new CustomMetaDataFieldCreateRequest - { - name = "Tst3", - label = "Test3" - }; - CustomMetaDataFieldSchemaObject schema = new CustomMetaDataFieldSchemaObject - { - type = "Number", - minValue = 1000, - maxValue = 3000 - - }; - model.schema = schema; - string url = "https://api.imagekit.io/v1/customMetadataFields"; - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Post)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .WithContent(reqJosn) - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.CreateCustomMetaDataFields(model); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void UpdateCustomMetaDataFields_ModelValidation() - { - string reqJosn = "{\n \"schema\": {\n \"type\": \"Number\",\n \"minValue\": 300,\n \"maxValue\": 500\n }\n}"; - CustomMetaDataFieldUpdateRequest requestUpdateModel = new CustomMetaDataFieldUpdateRequest - { - Id = "field_Id", - }; - CustomMetaDataFieldSchemaObject updateschema = new CustomMetaDataFieldSchemaObject - { - type = "Number", - minValue = 300, - maxValue = 500 - }; - requestUpdateModel.schema = updateschema; - - string url = "https://api.imagekit.io/v1/customMetadataFields/field_Id"; - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .WithContent(reqJosn) - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.UpdateCustomMetaDataFields(requestUpdateModel); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void DeleteCustomMetaDataField_ModelValidation() - { - string url = string.Format("https://api.imagekit.io/v1/customMetadataFields/{0}", "id"); - - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Delete)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.DeleteCustomMetaDataField("id"); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void DeleteFileVersion_ModelValidation() - { - DeleteFileVersionRequest model = new DeleteFileVersionRequest - { - fileId = "Tst3", - versionId = "Tst3" - }; - string url = string.Format("https://api.imagekit.io/v1/files/{0}/versions/{1}", model.fileId, - model.versionId); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Delete)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.DeleteFileVersion(model); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void CopyFile_ModelValidation() - { - string reqJosn = "{\"sourceFilePath\":\"Tst3\",\"destinationPath\":\"Tst3\",\"includeFileVersions\":false}"; - - CopyFileRequest model = new CopyFileRequest - { - sourceFilePath = "Tst3", - destinationPath = "Tst3" - }; - string url = string.Empty; - url = string.Format("https://api.imagekit.io/v1/files/copy"); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Post)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .WithContent(reqJosn) - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.CopyFile(model); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void CopyFile_includeFileVersions_ModelValidation() - { - string reqJosn = "{\"sourceFilePath\":\"Tst3\",\"destinationPath\":\"Tst3\",\"includeFileVersions\":true}"; - - CopyFileRequest model = new CopyFileRequest - { - sourceFilePath = "Tst3", - destinationPath = "Tst3", - includeFileVersions = true - }; - string url = string.Empty; - url = string.Format("https://api.imagekit.io/v1/files/copy"); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Post)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .WithContent(reqJosn) - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.CopyFile(model); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void MoveFile_ModelValidation() - { - string reqJosn = "{\"sourceFilePath\":\"Tst3\",\"destinationPath\":\"Tst3\"}"; - MoveFileRequest model = new MoveFileRequest - { - sourceFilePath = "Tst3", - destinationPath = "Tst3" - }; - - string url = string.Empty; - url = string.Format("https://api.imagekit.io/v1/files/move"); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Post)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .WithContent(reqJosn) - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.MoveFile(model); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void RenameFile_ModelValidation() - { - string reqJosn = "{\"filePath\":\"Tst3\",\"newFileName\":\"Tst4\",\"purgeCache\":false}"; - RenameFileRequest model = new RenameFileRequest - { - filePath = "Tst3", - newFileName = "Tst4", - purgeCache = false - }; - - string url = string.Empty; - url = string.Format("https://api.imagekit.io/v1/files/rename"); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Put)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .WithContent(reqJosn) - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.RenameFile(model); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void RenameFileTrue_ModelValidation() - { - string reqJosn = "{\"filePath\":\"Tst3\",\"newFileName\":\"Tst4\",\"purgeCache\":true}"; - RenameFileRequest model = new RenameFileRequest - { - filePath = "Tst3", - newFileName = "Tst4", - purgeCache = true - }; - - string url = string.Empty; - url = string.Format("https://api.imagekit.io/v1/files/rename"); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Put)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .WithContent(reqJosn) - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.RenameFile(model); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void RenameFileDefaultPurge_ModelValidation() - { - string reqJosn = "{\"filePath\":\"Tst3\",\"newFileName\":\"Tst4\",\"purgeCache\":false}"; - RenameFileRequest model = new RenameFileRequest - { - filePath = "Tst3", - newFileName = "Tst4" - }; - - string url = string.Empty; - url = string.Format("https://api.imagekit.io/v1/files/rename"); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Put)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .WithContent(reqJosn) - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.RenameFile(model); - mockHttp.VerifyNoOutstandingExpectation(); - } - [Fact] - public void CopyFolder_ModelValidation() - { - string reqJosn = "{\"sourceFolderPath\":\"Tst3\",\"destinationPath\":\"Tst3\",\"includeFileVersions\":false}"; - CopyFolderRequest model = new CopyFolderRequest - { - sourceFolderPath = "Tst3", - destinationPath = "Tst3" - - }; - string url = string.Format("https://api.imagekit.io/v1/bulkJobs/copyFolder"); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Post)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .WithContent(reqJosn) - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.CopyFolder(model); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void CopyFolder_includeFileVersions_ModelValidation() - { - string reqJosn = "{\"sourceFolderPath\":\"Tst3\",\"destinationPath\":\"Tst3\",\"includeFileVersions\":true}"; - CopyFolderRequest model = new CopyFolderRequest - { - sourceFolderPath = "Tst3", - destinationPath = "Tst3", - includeFileVersions = true - - }; - string url = string.Format("https://api.imagekit.io/v1/bulkJobs/copyFolder"); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Post)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .WithContent(reqJosn) - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.CopyFolder(model); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void MoveFolder_ModelValidation() - { - string reqJosn = "{\"sourceFolderPath\":\"Tst3\",\"destinationPath\":\"Tst3\"}"; - MoveFolderRequest model = new MoveFolderRequest - { - sourceFolderPath = "Tst3", - destinationPath = "Tst3" - }; - string url = string.Format("https://api.imagekit.io/v1/bulkJobs/moveFolder"); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Post)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .WithContent(reqJosn) - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.MoveFolder(model); - mockHttp.VerifyNoOutstandingExpectation(); - } - - - [Fact] - public void GetBulkJobStatus_ModelValidation() - { - string url = string.Format("https://api.imagekit.io/v1/bulkJobs/{0}", "jobId"); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Get)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.GetBulkJobStatus("jobId"); - mockHttp.VerifyNoOutstandingExpectation(); - } - [Fact] - public void GetFileVersions_ModelValidation() - { - string url = string.Format("https://api.imagekit.io/v1/files/{0}/versions", "fileId"); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Get)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.GetFileVersions("fileId"); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void GetFileVersionDetails_ModelValidation() - { - string url = string.Format("https://api.imagekit.io/v1/files/{0}/versions/{1}", "fileId", "versionId"); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Get)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.GetFileVersionDetails("fileId", "versionId"); - mockHttp.VerifyNoOutstandingExpectation(); - } - - [Fact] - public void RestoreFileVersion_ModelValidation() - { - string url = string.Format("https://api.imagekit.io/v1/files/{0}/versions/{1}/restore", "fileId", "versionId"); - var mockHttp = new MockHttpMessageHandler(); - var request = mockHttp.Expect(url) - .With(a => a.Method.Equals(HttpMethod.Put)) - .WithHeaders("Authorization: Basic cHJpdmF0ZV9rZXk6") - .Respond("application/json", "{'name' : 'ImageKit Response'}"); - - var client = mockHttp.ToHttpClient(); - RestClient rs = new RestClient(GOOD_PRIVATEKEY, GOOD_URLENDPOINT, client); - var response = rs.RestoreFileVersion("fileId", "versionId"); - mockHttp.VerifyNoOutstandingExpectation(); - } - } -} diff --git a/Imagekit.UnitTests/ImageKitTestCasesAsync.cs b/Imagekit.UnitTests/ImageKitTestCasesAsync.cs deleted file mode 100644 index e2722327..00000000 --- a/Imagekit.UnitTests/ImageKitTestCasesAsync.cs +++ /dev/null @@ -1,726 +0,0 @@ -using Imagekit.Constant; -using Imagekit.Sdk; -using Imagekit.Models; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using Newtonsoft.Json.Linq; -using Xunit; - -namespace Imagekit.UnitTests -{ - - public class ImageKitTestCasesAsync - { - private const string GOOD_PUBLICKEY = "abc"; - private const string GOOD_PRIVATEKEY = "abc"; - private const string GOOD_URLENDPOINT = "https://dasdsad.dad.io/"; - - [Fact] - public void Missing_Key_Exception() - { - - Exception actualException = Assert.Throws(() => new RestClient("", "https://dasdsad.dad.io/", new HttpClient())); - Assert.Equal(ErrorMessages.InvalidKey, actualException.Message); - } - [Fact] - public void Missing_URL_Exception() - { - - Exception actualException = Assert.Throws(() => new RestClient("abc", "", new HttpClient())); - Assert.Equal(ErrorMessages.InvalidApiUrl, actualException.Message); - } - - [Fact] - public void Constructor_TransformationPosition_Default() - { - var imagekit = new ImagekitClient(GOOD_PUBLICKEY, GOOD_PRIVATEKEY, GOOD_URLENDPOINT); - Assert.NotNull(imagekit); - } - - [Fact] - public void GetFileRequest_Default() - { - GetFileListRequest ob = new GetFileListRequest - { - Type = "file", - Limit = 10, - Skip = 0, - Sort = "ASC_CREATED", - SearchQuery = "createdAt >= \"7d\"", - FileType = "image", - Path = "/" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (ResultList)restClient.GetFileListRequestAsync(ob).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj.Raw); - responseObj1 = JToken.Parse(responseObj1).ToString(); - - Assert.Equal(responseObj.Raw, responseObj1); - } - [Fact] - public async void GetFileRequestByNameWithoutSearchQuery() - { - GetFileListRequest ob = new GetFileListRequest - { - - Name = "file_name.jpg", - - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = await restClient.GetFileListRequestAsync(ob); - // Console.WriteLine("res",response); - var responseObj1 = JsonConvert.SerializeObject(responseObj.Raw); - responseObj1 = JToken.Parse(responseObj1).ToString(); - - Assert.Equal(responseObj.Raw, responseObj1); - Console.WriteLine(responseObj.Raw); - } - [Fact] - public void GetFileRequestByName() - { - GetFileListRequest ob = new GetFileListRequest - { - - SearchQuery = "name = \"file_name.jpg\"", - - - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (ResultList)restClient.GetFileListRequestAsync(ob).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj.Raw); - responseObj1 = JToken.Parse(responseObj1).ToString(); - - Assert.Equal(responseObj.Raw, responseObj1); - } - [Fact] - public void GetFileRequestByTag() - { - GetFileListRequest ob = new GetFileListRequest - { - - SearchQuery = "tags = \"tag1,tag2\"", - - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (ResultList)restClient.GetFileListRequestAsync(ob).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj.Raw); - responseObj1 = JToken.Parse(responseObj1).ToString(); - - Assert.Equal(responseObj.Raw, responseObj1); - } - [Fact] - public void GetFileRequestWithoutFilter_Default() - { - GetFileListRequest ob = new GetFileListRequest(); - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (ResultList)restClient.GetFileListRequestAsync(ob).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj.Raw); - responseObj1 = JToken.Parse(responseObj1).ToString(); - - Assert.Equal(responseObj.Raw, responseObj1); - } - - [Fact] - public void GetFileDetail_Default() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (Result)restClient.GetFileDetailAsync("abc").Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void GetFile_ID_Detail_Exception() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.GetFileDetailAsync("")); - Assert.Equal(ErrorMessages.FileIdMissing, ex.Result.Message); - } - - [Fact] - public void PurgeCache_Default() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.PurgeCacheAsync("abc").Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void PurgeStatus_Default() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.PurgeStatusAsync("abc").Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void DeleteFile_Default() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (ResultDelete)restClient.DeleteFileAsync("abc").Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void Delete_File_ID_Exception() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.DeleteFileAsync("")); - Assert.Equal(ErrorMessages.FileIdMissing, ex.Result.Message); - } - - - [Fact] - public void BulkDeleteFiles_Default() - { - List ob = new List - { - "abc", - "abcd" - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (ResultFileDelete)restClient.BulkDeleteFilesAsync(ob).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj.Raw); - responseObj1=JToken.Parse(responseObj1).ToString(); - Assert.Equal(responseObj.Raw, responseObj1); - } - [Fact] - public void Bulk_Delete_Files_Input_Missing_Exception() - { - List ob = new List(); - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.BulkDeleteFilesAsync(ob)); - Assert.Equal(ErrorMessages.ListFilesInputMissing, ex.Result.Message); - } - - [Fact] - public void Missing_Filed_Null_Exception() - { - TagsRequest ob = new TagsRequest - { - tags = new List { "abc" }, - fileIds = null - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.ManageTagsAsync(ob, "")); - Assert.Equal(ErrorMessages.InvalidFiledParamValue, ex.Result.Message); - } - - - - - [Fact] - public void Missing_Remove_Filed_Null_Exception() - { - TagsRequest ob = new TagsRequest - { - tags = new List { "abc" }, - fileIds = null - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.ManageTagsAsync(ob, "")); - Assert.Equal(ErrorMessages.InvalidFiledParamValue, ex.Result.Message); - } - - - [Fact] - public void Missing_AI_Filed_Null_Exception() - { - AITagsRequest ob = new AITagsRequest - { - AITags = new List { "abc" }, - fileIds = null - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.RemoveAITagsAsync(ob)); - Assert.Equal(ErrorMessages.InvalidFiledParamValue, ex.Result.Message); - } - - [Fact] - public void DeleteFileVersion_Default() - { - DeleteFileVersionRequest model = new DeleteFileVersionRequest - { - fileId = "Tst3", - versionId = "Tst3" - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.DeleteFileVersionAsync(model).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void Missing_Object_FileVersionException() - { - DeleteFileVersionRequest model = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.DeleteFileVersionAsync(model)); - Assert.Equal(ErrorMessages.InvalidDelVerValue, ex.Result.Message); - } - [Fact] - public void Missing_fileId_FileVersionException() - { - DeleteFileVersionRequest model = new DeleteFileVersionRequest - { - versionId = "abc" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.DeleteFileVersionAsync(model)); - Assert.Equal(ErrorMessages.InvalidFieldIdDelVerValue, ex.Result.Message); - } - - [Fact] - public void Missing_versionId_FileVersionException() - { - DeleteFileVersionRequest model = new DeleteFileVersionRequest - { - versionId = "", - fileId = "sas" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.DeleteFileVersionAsync(model)); - Assert.Equal(ErrorMessages.InvalidversionIdDelVerValue, ex.Result.Message); - } - - [Fact] - public void CopyFile_Default() - { - CopyFileRequest model = new CopyFileRequest - { - sourceFilePath = "Tst3", - destinationPath = "Tst3", - includeFileVersions = true - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.CopyFileAsync(model).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void Missing_Obj_CopyFileException() - { - CopyFileRequest model = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.CopyFileAsync(model)); - Assert.Equal(ErrorMessages.InvalidCopyValue, ex.Result.Message); - } - - - - [Fact] - public void Missing_Source_CopyFileException() - { - CopyFileRequest model = new CopyFileRequest - { - destinationPath = "abc" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.CopyFileAsync(model)); - Assert.Equal(ErrorMessages.InvalidSourceValue, ex.Result.Message); - } - - [Fact] - public void Missing_Destination_CopyFileException() - { - CopyFileRequest model = new CopyFileRequest - { - sourceFilePath = "abc" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.CopyFileAsync(model)); - Assert.Equal(ErrorMessages.InvalidDestinationValue, ex.Result.Message); - } - - [Fact] - public void MoveFile_Default() - { - MoveFileRequest model = new MoveFileRequest - { - sourceFilePath = "Tst3", - destinationPath = "Tst3" - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.MoveFileAsync(model).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void Missing_Obj_MoveFileException() - { - MoveFileRequest model = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.MoveFileAsync(model)); - Assert.Equal(ErrorMessages.InvalidCopyValue, ex.Result.Message); - } - - [Fact] - public void Missing_Source_MoveFileException() - { - MoveFileRequest model = new MoveFileRequest - { - destinationPath = "abc" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.MoveFileAsync(model)); - Assert.Equal(ErrorMessages.InvalidSourceValue, ex.Result.Message); - } - - [Fact] - public void Missing_Destination_MoveFileException() - { - MoveFileRequest model = new MoveFileRequest - { - sourceFilePath = "abc" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.MoveFileAsync(model)); - Assert.Equal(ErrorMessages.InvalidDestinationValue, ex.Result.Message); - } - - [Fact] - public void RenameFile_Default() - { - RenameFileRequest model = new RenameFileRequest - { - filePath = "Tst3", - newFileName = "Tst4", - purgeCache = false - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.RenameFileAsync(model).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void Missing_FilePath_RenameFileException() - { - RenameFileRequest model = new RenameFileRequest - { - newFileName = "test" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.RenameFileAsync(model)); - Assert.Equal(ErrorMessages.InvalidRenameFilePathValue, ex.Result.Message); - } - [Fact] - public void Missing_NewFileName_RenameFileException() - { - RenameFileRequest model = new RenameFileRequest - { - filePath = "test" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.RenameFileAsync(model)); - Assert.Equal(ErrorMessages.InvalidRenameNewFileNameValue, ex.Result.Message); - } - - - [Fact] - public void GetBulkJobStatus_Default() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.GetBulkJobStatusAsync("abc").Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void Missing_Job_Id_BulkJobStatusException() - { - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.GetBulkJobStatusAsync("")); - Assert.Equal(ErrorMessages.InvalidJobValue, ex.Result.Message); - } - } -} - - - - diff --git a/Imagekit.UnitTests/ImageKitTestCasesNonAsync.cs b/Imagekit.UnitTests/ImageKitTestCasesNonAsync.cs deleted file mode 100644 index 4e57c7be..00000000 --- a/Imagekit.UnitTests/ImageKitTestCasesNonAsync.cs +++ /dev/null @@ -1,734 +0,0 @@ -using Imagekit.Constant; -using Imagekit.Sdk; -using Imagekit.Models; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using Xunit; -using Newtonsoft.Json.Linq; - -namespace Imagekit.UnitTests.FileVersion -{ - public class ImageKitTestCasesNonAsync - { - private const string GOOD_PUBLICKEY = "public_key"; - private const string GOOD_PRIVATEKEY = "private_key_test"; - private const string GOOD_URLENDPOINT = "https://endpoint_url.io/"; - - - [Fact] - public void Missing_Key_ExceptionNonAsync() - { - - Exception actualException = Assert.Throws(() => new RestClient("", "https://endpoint_url.io/", new HttpClient())); - Assert.Equal(ErrorMessages.InvalidKey, actualException.Message); - } - [Fact] - public void Missing_URL_ExceptionNonAsync() - { - - Exception actualException = Assert.Throws(() => new RestClient("abc", "", new HttpClient())); - Assert.Equal(ErrorMessages.InvalidApiUrl, actualException.Message); - } - - [Fact] - public void Constructor_TransformationPosition_DefaultNonAsync() - { - var imagekit = new ImagekitClient(GOOD_PUBLICKEY, GOOD_PRIVATEKEY, GOOD_URLENDPOINT); - Assert.NotNull(imagekit); - } - [Fact] - public void UrlValidation() - { - var imagekit = new ImagekitClient(GOOD_PUBLICKEY, GOOD_PRIVATEKEY, GOOD_URLENDPOINT); - string path = "/default-image.jpg"; - Transformation trans = new Transformation() - .Width(400) - .Height(300) - .AspectRatio("4-3") - .Quality(40) - .Crop("force").CropMode("extract"). - Focus("left"). - Format("jpeg"). - Background("A94D34"). - Border("5-A94D34"). - Rotation(90). - Blur(10). - Named("some_name"). - Progressive(true). - Lossless(true). - Trim(5). - Metadata(true). - ColorProfile(true). - DefaultImage("folder/file.jpg/"). //trailing slash case - Dpr(3). - EffectSharpen(10). - EffectUsm("2-2-0.8-0.024"). - EffectContrast(true). - EffectGray(). - EffectShadow("bl-15"). - EffectGradient("from-lightskyblue_to-mintcream"). - Original(). - Raw("l-text,i-Imagekit,fs-50,l-end") - ; - - string imageURL = imagekit.Url(trans).Path(path).TransformationPosition("query").Generate(); - - Assert.Equal("https://endpoint_url.io/default-image.jpg?tr=w-400%2Ch-300%2Car-4-3%2Cq-40%2Cc-force%2Ccm-extract%2Cfo-left%2Cf-jpeg%2Cbg-A94D34%2Cb-5-A94D34%2Crt-90%2Cbl-10%2Cn-some_name%2Cpr-true%2Clo-true%2Ct-5%2Cmd-true%2Ccp-true%2Cdi-folder%40%40file.jpg%2Cdpr-3%2Ce-sharpen-10%2Ce-usm-2-2-0.8-0.024%2Ce-contrast-true%2Ce-grayscale-true%2Ce-shadow-bl-15%2Ce-gradient-from-lightskyblue_to-mintcream%2Corig-true%2Cl-text%2Ci-Imagekit%2Cfs-50%2Cl-end", imageURL); - } - - - [Fact] - public void UrlValidation1() - { - var imagekit = new ImagekitClient(GOOD_PUBLICKEY, GOOD_PRIVATEKEY, GOOD_URLENDPOINT); - var imgUrl1 = "https://ik.imagekit.io/demo/default-image.jpg"; - string[] queryParams = { "b=query", "a=value" }; - try - { - var signedUrl = imagekit.Url(new Transformation().Width(400).Height(300)) - .Src(imgUrl1) - .QueryParameters(queryParams) - .ExpireSeconds(600) - .Signed() - .Generate(); - Console.WriteLine("Signed Url for first image transformed with height: 300, width: 400: - {0}", signedUrl); - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - } - } - - [Fact] - public void GetFileRequest_DefaultNonAsync() - { - GetFileListRequest ob = new GetFileListRequest - { - Limit = 10, - Skip = 0 - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (ResultList)restClient.GetFileListRequest(ob); - - Assert.Equal(response.Raw, response.Raw); - } - - [Fact] - public void GetFileDetail_DefaultNonAsync() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var response = (Result)restClient.GetFileDetail("abc"); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void GetFile_ID_Detail_ExceptionNonAsync() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.GetFileDetail("")); - Assert.Equal(ErrorMessages.FileIdMissing, ex.Message); - } - - - - [Fact] - public void PurgeCache_DefaultNonAsync() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.PurgeCache("abc"); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void PurgeStatus_DefaultNonAsync() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.PurgeStatus("abc"); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void DeleteFile_DefaultNonAsync() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (ResultDelete)restClient.DeleteFile("abc"); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void Delete_File_ID_ExceptionNonAsync() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.DeleteFile("")); - Assert.Equal(ErrorMessages.FileIdMissing, ex.Message); - } - - - [Fact] - public void BulkDeleteFiles_DefaultNonAsync() - { - List ob = new List - { - "abc", - "abcd" - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (ResultFileDelete)restClient.BulkDeleteFiles(ob); - var responseObj1 = JsonConvert.SerializeObject(responseObj.Raw); - responseObj1 = JToken.Parse(responseObj1).ToString(); - - Assert.Equal(responseObj.Raw, responseObj1); - } - [Fact] - public void Bulk_Delete_Files_Input_Missing_ExceptionNonAsync() - { - List ob = new List(); - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.BulkDeleteFiles(ob)); - Assert.Equal(ErrorMessages.ListFilesInputMissing, ex.Message); - } - - - - - - - - [Fact] - public void Missing_Filed_Null_ExceptionNonAsync() - { - TagsRequest ob = new TagsRequest - { - tags = new List { "abc" }, - fileIds = null - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.ManageTags(ob, "")); - Assert.Equal(ErrorMessages.InvalidFiledParamValue, ex.Message); - } - - - - - [Fact] - public void Missing_Remove_Filed_Null_ExceptionNonAsync() - { - TagsRequest ob = new TagsRequest - { - tags = new List { "abc" }, - fileIds = null - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.ManageTags(ob, "")); - Assert.Equal(ErrorMessages.InvalidFiledParamValue, ex.Message); - } - - - [Fact] - public void Missing_AI_Filed_Null_ExceptionNonAsync() - { - AITagsRequest ob = new AITagsRequest - { - AITags = new List { "abc" }, - fileIds = null - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.RemoveAITags(ob)); - Assert.Equal(ErrorMessages.InvalidFiledParamValue, ex.Message); - } - - [Fact] - public void DeleteFileVersion_DefaultNonAsync() - { - DeleteFileVersionRequest model = new DeleteFileVersionRequest - { - fileId = "Tst3", - versionId = "Tst3" - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.DeleteFileVersion(model); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void Missing_Object_FileVersionExceptionNonAsync() - { - DeleteFileVersionRequest model = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => (ResultNoContent)restClient.DeleteFileVersion(model)); - Assert.Equal(ErrorMessages.InvalidDelVerValue, ex.Message); - } - [Fact] - public void Missing_fileId_FileVersionExceptionNonAsync() - { - DeleteFileVersionRequest model = new DeleteFileVersionRequest - { - versionId = "abc" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.DeleteFileVersion(model)); - Assert.Equal(ErrorMessages.InvalidFieldIdDelVerValue, ex.Message); - } - - [Fact] - public void Missing_versionId_FileVersionExceptionNonAsync() - { - DeleteFileVersionRequest model = new DeleteFileVersionRequest - { - versionId = "", - fileId = "sas" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.DeleteFileVersion(model)); - Assert.Equal(ErrorMessages.InvalidversionIdDelVerValue, ex.Message); - } - - [Fact] - public void CopyFile_DefaultNonAsync() - { - CopyFileRequest model = new CopyFileRequest - { - sourceFilePath = "Tst3", - destinationPath = "Tst3", - includeFileVersions = true - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.CopyFile(model); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void Missing_Obj_CopyFileExceptionNonAsync() - { - CopyFileRequest model = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.CopyFile(model)); - Assert.Equal(ErrorMessages.InvalidCopyValue, ex.Message); - } - - - - [Fact] - public void Missing_Source_CopyFileExceptionNonAsync() - { - CopyFileRequest model = new CopyFileRequest - { - destinationPath = "abc" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.CopyFile(model)); - Assert.Equal(ErrorMessages.InvalidSourceValue, ex.Message); - } - - [Fact] - public void Missing_Destination_CopyFileExceptionNonAsync() - { - CopyFileRequest model = new CopyFileRequest - { - sourceFilePath = "abc" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.CopyFile(model)); - Assert.Equal(ErrorMessages.InvalidDestinationValue, ex.Message); - } - - [Fact] - public void MoveFile_DefaultNonAsync() - { - MoveFileRequest model = new MoveFileRequest - { - sourceFilePath = "Tst3", - destinationPath = "Tst3" - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.MoveFile(model); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void Missing_Obj_MoveFileExceptionNonAsync() - { - MoveFileRequest model = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.MoveFile(model)); - Assert.Equal(ErrorMessages.InvalidCopyValue, ex.Message); - } - - [Fact] - public void Missing_Source_MoveFileExceptionNonAsync() - { - MoveFileRequest model = new MoveFileRequest - { - destinationPath = "abc" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.MoveFile(model)); - Assert.Equal(ErrorMessages.InvalidSourceValue, ex.Message); - } - - [Fact] - public void Missing_Destination_MoveFileExceptionNonAsync() - { - MoveFileRequest model = new MoveFileRequest - { - sourceFilePath = "abc" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.MoveFile(model)); - Assert.Equal(ErrorMessages.InvalidDestinationValue, ex.Message); - } - - [Fact] - public void RenameFile_DefaultNonAsync() - { - RenameFileRequest model = new RenameFileRequest - { - filePath = "Tst3", - newFileName = "Tst4", - purgeCache = false - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.RenameFile(model); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void Missing_FilePath_RenameFileExceptionNonAsync() - { - RenameFileRequest model = new RenameFileRequest - { - newFileName = "test" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.RenameFile(model)); - Assert.Equal(ErrorMessages.InvalidRenameFilePathValue, ex.Message); - } - [Fact] - public void Missing_NewFileName_RenameFileExceptionNonAsync() - { - RenameFileRequest model = new RenameFileRequest - { - filePath = "test" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.RenameFile(model)); - Assert.Equal(ErrorMessages.InvalidRenameNewFileNameValue, ex.Message); - } - - - [Fact] - public void GetBulkJobStatus_DefaultNonAsync() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.GetBulkJobStatus("abc"); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void Missing_Job_Id_BulkJobStatusExceptionNonAsync() - { - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.GetBulkJobStatus("")); - Assert.Equal(ErrorMessages.InvalidJobValue, ex.Message); - } - - - [Fact] - public void SameImage_getHammingDistance_expectedSuccessWith() - { - var imagekit = new ImagekitClient(GOOD_PUBLICKEY, GOOD_PRIVATEKEY, GOOD_URLENDPOINT); - int hammingDistance = imagekit.PHashDistance("f06830ca9f1e3e90", "f06830ca9f1e3e90"); - Assert.Equal(0, hammingDistance); - } - - - [Fact] - public void SimilarImage_getHammingDistance_expectedSuccessWith() - { - var imagekit = new ImagekitClient(GOOD_PUBLICKEY, GOOD_PRIVATEKEY, GOOD_URLENDPOINT); - int hammingDistance = imagekit.PHashDistance("33699c96619cc69e", "968e978414fe04ea"); - Assert.Equal(2, hammingDistance); - } - - [Fact] - public void AuthenticationParamCheck() - { - var imagekit = new ImagekitClient(GOOD_PUBLICKEY, GOOD_PRIVATEKEY, GOOD_URLENDPOINT); - string token = "your_token"; - string expire = "1582269249"; - AuthParamResponse authParams = imagekit.GetAuthenticationParameters(token, expire); - Assert.Equal(token, authParams.token); - Assert.Equal(expire, authParams.expire); - Assert.Equal("e71bcd6031016b060d349d212e23e85c791decdd", authParams.signature); - } - - [Fact] - public void AuthenticationParamNullCheck() - { - var imagekit = new ImagekitClient(GOOD_PUBLICKEY, GOOD_PRIVATEKEY, GOOD_URLENDPOINT); - string token = null; - string expire = "1582269249"; - AuthParamResponse authParams = imagekit.GetAuthenticationParameters(token, expire); - Assert.NotEmpty(authParams.token); - Assert.IsType(authParams.token); - Assert.Equal(expire, authParams.expire); - Assert.NotEmpty(authParams.signature); - Assert.IsType(authParams.signature); - } - } -} - - - - diff --git a/Imagekit.UnitTests/Imagekit.UnitTests.csproj b/Imagekit.UnitTests/Imagekit.UnitTests.csproj deleted file mode 100644 index 0c21e082..00000000 --- a/Imagekit.UnitTests/Imagekit.UnitTests.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net7.0;net5.0;net6.0;netcoreapp3.1 - 8 - true - false - - - - 1701;1702;NU5125;NU5048; - - - - 1701;1702;NU5125;NU5048; - - - - 1701;1702;NU5125;NU5048; - 5 - - - - 1701;1702;NU5125;NU5048; - 5 - - - - 1701;1702;NU5125;NU5048; - 5 - - - - 1701;1702;NU5125;NU5048; - 5 - - - - 1701;1702;NU5125;NU5048; - - - - 1701;1702;NU5125;NU5048; - - - - 1701;1702;NU5125;NU5048; - - - - 1701;1702;NU5125;NU5048; - - - - - - - - - - - - - - - - - - - - - - - Always - - - diff --git a/Imagekit.UnitTests/ManageFolder/ManageFolderTestAsync.cs b/Imagekit.UnitTests/ManageFolder/ManageFolderTestAsync.cs deleted file mode 100644 index 1d2e829f..00000000 --- a/Imagekit.UnitTests/ManageFolder/ManageFolderTestAsync.cs +++ /dev/null @@ -1,299 +0,0 @@ -using Imagekit.Constant; -using Imagekit.Models; -using Newtonsoft.Json; -using System; -using System.Net; -using System.Net.Http; -using Xunit; - - -namespace Imagekit.UnitTests -{ - - public class ManageFolderTestAsync - - { - private const string GOOD_PUBLICKEY = "abc"; - private const string GOOD_URLENDPOINT = "https://dasdsad.dad.io/"; - - - [Fact] - public void CreateFolder_Default() - { - CreateFolderRequest model = new CreateFolderRequest - { - folderName = "Tst3", - parentFolderPath = "Tst3" - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.CreateFolderAsync(model).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void CreateFolderException() - { - CreateFolderRequest model = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.CreateFolderAsync(model)); - Assert.Equal(ErrorMessages.InvalidCreateFolderValue, ex.Result.Message); - } - - [Fact] - public void Missing_folderName_Exception() - { - CreateFolderRequest model = new CreateFolderRequest - { - parentFolderPath = "test" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.CreateFolderAsync(model)); - Assert.Equal(ErrorMessages.InvalidfolderNameValue, ex.Result.Message); - } - [Fact] - public void Missing_parentFolderPath_FolderException() - { - CreateFolderRequest model = new CreateFolderRequest - { - folderName = "test" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.CreateFolderAsync(model)); - Assert.Equal(ErrorMessages.InvalidFolderPathValue, ex.Result.Message); - } - [Fact] - public void DeleteFolder_Default() - { - DeleteFolderRequest model = new DeleteFolderRequest - { - folderPath = "Tst3" - }; - - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.DeleteFolderAsync(model).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void Missing_Folder_Path_Exception() - { - DeleteFolderRequest model = new DeleteFolderRequest(); - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.DeleteFolderAsync(model)); - Assert.Equal(ErrorMessages.InvalidDelFolderValue, ex.Result.Message); - } - - - [Fact] - public void CopyFolder_Default() - { - CopyFolderRequest model = new CopyFolderRequest - { - sourceFolderPath = "Tst3", - destinationPath = "Tst3", - includeFileVersions = true - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.CopyFolderAsync(model).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void Missing_Obj_FolderException() - { - CopyFolderRequest model = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.CopyFolderAsync(model)); - Assert.Equal(ErrorMessages.InvalidCopyFolderValue, ex.Result.Message); - } - [Fact] - public void Missing_sourceFolderPath_FolderException() - { - CopyFolderRequest model = new CopyFolderRequest - { - destinationPath = "abc" - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.CopyFolderAsync(model)); - Assert.Equal(ErrorMessages.InvalidCopysourceFolderPathValue, ex.Result.Message); - } - [Fact] - public void Missing_Destination_FolderPath_FolderException() - { - CopyFolderRequest model = new CopyFolderRequest - { - sourceFolderPath = "abc" - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.CopyFolderAsync(model)); - Assert.Equal(ErrorMessages.InvalidCopydestinationPathValue, ex.Result.Message); - } - - [Fact] - public void MoveFolder_Default() - { - MoveFolderRequest model = new MoveFolderRequest - { - sourceFolderPath = "Tst3", - destinationPath = "Tst3" - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.MoveFolderAsync(model).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void Missing_Obj_Move_FolderException() - { - MoveFolderRequest model = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.MoveFolderAsync(model)); - Assert.Equal(ErrorMessages.InvalidCopyFolderValue, ex.Result.Message); - } - - [Fact] - public void Missing_sourceFolderPath_MoveFolderException() - { - MoveFolderRequest model = new MoveFolderRequest - { - destinationPath = "abc" - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.MoveFolderAsync(model)); - Assert.Equal(ErrorMessages.InvalidCopysourceFolderPathValue, ex.Result.Message); - } - [Fact] - public void Missing_Destination_FolderPath_MoveFolderException() - { - MoveFolderRequest model = new MoveFolderRequest - { - sourceFolderPath = "abc" - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.MoveFolderAsync(model)); - Assert.Equal(ErrorMessages.InvalidCopydestinationPathValue, ex.Result.Message); - } - - } -} - - - - diff --git a/Imagekit.UnitTests/ManageFolder/ManageFolderTestNonAsync.cs b/Imagekit.UnitTests/ManageFolder/ManageFolderTestNonAsync.cs deleted file mode 100644 index fb96b647..00000000 --- a/Imagekit.UnitTests/ManageFolder/ManageFolderTestNonAsync.cs +++ /dev/null @@ -1,296 +0,0 @@ -using Imagekit.Constant; -using Imagekit.Models; -using Newtonsoft.Json; -using System; -using System.Net; -using System.Net.Http; -using Xunit; - - -namespace Imagekit.UnitTests -{ - - public class ManageFolderTestNon - - { - private const string GoodPublickey = "abc"; - private const string GoodUrlendpoint = "https://dasdsad.dad.io/"; - - - [Fact] - public void CreateFolder_DefaultNonAsync() - { - CreateFolderRequest model = new CreateFolderRequest - { - folderName = "Tst3", - parentFolderPath = "Tst3" - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GoodPublickey, GoodUrlendpoint, httpClient); - - var response = restClient.CreateFolder(model); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void CreateFolderExceptionNonAsync() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GoodPublickey, GoodUrlendpoint, httpClient); - var ex = Assert.Throws(() => restClient.CreateFolder(null)); - Assert.Equal(ErrorMessages.InvalidCreateFolderValue, ex.Message); - } - - [Fact] - public void Missing_folderName_ExceptionNonAsync() - { - CreateFolderRequest model = new CreateFolderRequest - { - parentFolderPath = "test" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GoodPublickey, GoodUrlendpoint, httpClient); - var ex = Assert.Throws(() => restClient.CreateFolder(model)); - Assert.Equal(ErrorMessages.InvalidfolderNameValue, ex.Message); - } - [Fact] - public void Missing_parentFolderPath_FolderExceptionNonAsync() - { - CreateFolderRequest model = new CreateFolderRequest - { - folderName = "test" - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GoodPublickey, GoodUrlendpoint, httpClient); - var ex = Assert.Throws(() => restClient.CreateFolder(model)); - Assert.Equal(ErrorMessages.InvalidFolderPathValue, ex.Message); - } - [Fact] - public void DeleteFolder_DefaultNonAsync() - { - DeleteFolderRequest model = new DeleteFolderRequest - { - folderPath = "Tst3" - }; - - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GoodPublickey, GoodUrlendpoint, httpClient); - - var response = restClient.DeleteFolder(model); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void Missing_Folder_Path_ExceptionNonAsync() - { - DeleteFolderRequest model = new DeleteFolderRequest(); - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GoodPublickey, GoodUrlendpoint, httpClient); - var ex = Assert.Throws(() => restClient.DeleteFolder(model)); - Assert.Equal(ErrorMessages.InvalidDelFolderValue, ex.Message); - } - - - [Fact] - public void CopyFolder_DefaultNonAsync() - { - CopyFolderRequest model = new CopyFolderRequest - { - sourceFolderPath = "Tst3", - destinationPath = "Tst3", - includeFileVersions = true - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GoodPublickey, GoodUrlendpoint, httpClient); - - var response = restClient.CopyFolder(model); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void Missing_Obj_FolderExceptionNonAsync() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GoodPublickey, GoodUrlendpoint, httpClient); - var ex = Assert.Throws(() => restClient.CopyFolder(null)); - Assert.Equal(ErrorMessages.InvalidCopyFolderValue, ex.Message); - } - [Fact] - public void Missing_sourceFolderPath_FolderExceptionNonAsync() - { - CopyFolderRequest model = new CopyFolderRequest - { - destinationPath = "abc" - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GoodPublickey, GoodUrlendpoint, httpClient); - var ex = Assert.Throws(() => restClient.CopyFolder(model)); - Assert.Equal(ErrorMessages.InvalidCopysourceFolderPathValue, ex.Message); - } - [Fact] - public void Missing_Destination_FolderPath_FolderExceptionNonAsync() - { - CopyFolderRequest model = new CopyFolderRequest - { - sourceFolderPath = "abc" - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GoodPublickey, GoodUrlendpoint, httpClient); - var ex = Assert.Throws(() => restClient.CopyFolder(model)); - Assert.Equal(ErrorMessages.InvalidCopydestinationPathValue, ex.Message); - } - - [Fact] - public void MoveFolder_DefaultNonAsync() - { - MoveFolderRequest model = new MoveFolderRequest - { - sourceFolderPath = "Tst3", - destinationPath = "Tst3" - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GoodPublickey, GoodUrlendpoint, httpClient); - - var response = restClient.MoveFolder(model); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void Missing_Obj_Move_FolderExceptionNonAsync() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GoodPublickey, GoodUrlendpoint, httpClient); - var ex = Assert.Throws(() => restClient.MoveFolder(null)); - Assert.Equal(ErrorMessages.InvalidCopyFolderValue, ex.Message); - } - - [Fact] - public void Missing_sourceFolderPath_MoveFolderExceptionNonAsync() - { - MoveFolderRequest model = new MoveFolderRequest - { - destinationPath = "abc" - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GoodPublickey, GoodUrlendpoint, httpClient); - var ex = Assert.Throws(() => restClient.MoveFolder(model)); - Assert.Equal(ErrorMessages.InvalidCopysourceFolderPathValue, ex.Message); - } - [Fact] - public void Missing_Destination_FolderPath_MoveFolderExceptionNonAsync() - { - MoveFolderRequest model = new MoveFolderRequest - { - sourceFolderPath = "abc" - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GoodPublickey, GoodUrlendpoint, httpClient); - var ex = Assert.Throws(() => restClient.MoveFolder(model)); - Assert.Equal(ErrorMessages.InvalidCopydestinationPathValue, ex.Message); - } - - } -} - - - - diff --git a/Imagekit.UnitTests/MetaData/MetaDataTestAsync.cs b/Imagekit.UnitTests/MetaData/MetaDataTestAsync.cs deleted file mode 100644 index af0f8d52..00000000 --- a/Imagekit.UnitTests/MetaData/MetaDataTestAsync.cs +++ /dev/null @@ -1,557 +0,0 @@ -using Imagekit.Constant; -using Imagekit.Models; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using Imagekit.Models.Response; -using Xunit; -using static Imagekit.Models.CustomMetaDataFieldSchemaObject; -using Newtonsoft.Json.Linq; - -namespace Imagekit.UnitTests.MetaData -{ - - public class MetaDataTestAsync - { - private const string GOOD_PUBLICKEY = "abc"; - private const string GOOD_URLENDPOINT = "https://dasdsad.dad.io/"; - - - - [Fact] - public void GetFileMetadata_Default() - { - - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (ResultMetaData)restClient.GetFileMetaDataAsync("abc").Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj.Raw); - responseObj1 = JToken.Parse(responseObj1).ToString(); - - Assert.Equal(responseObj.Raw, responseObj1); - } - [Fact] - public void GetFileMetadataException() - { - List ob = new List(); - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.GetFileMetaData("")); - Assert.Equal(ErrorMessages.FileIdMissing, ex.Message); - } - - - [Fact] - public void GetRemoteFileMetadata_Default() - { - - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (ResultMetaData)restClient.GetRemoteFileMetaDataAsync("abc").Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj.Raw); - responseObj1 = JToken.Parse(responseObj1).ToString(); - - Assert.Equal(responseObj.Raw, responseObj1); - } - [Fact] - public void GetRemoteFileMetadataException() - { - List ob = new List(); - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.GetRemoteFileMetaDataAsync("")); - Assert.Equal(ErrorMessages.InvalidUrlValue, ex.Result.Message); - } - - - [Fact] - public void GetCustomMetaDataFields_Default() - { - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.GetCustomMetaDataFieldsAsync(true).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void CreateCustomMetaDataFields_Default() - { - CustomMetaDataFieldCreateRequest model = new CustomMetaDataFieldCreateRequest - { - name = "Tst3", - label = "Test3" - }; - CustomMetaDataFieldSchemaObject schema = new CustomMetaDataFieldSchemaObject - { - type = "Number", - minValue = 1000, - maxValue = 3000, - minLength = 500, - maxLength = 600, - isValueRequired = false - }; - - model.schema = schema; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.CreateCustomMetaDataFieldsAsync(model).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void CreateCustomMetaDataFields_Type_Date() - { - CustomMetaDataFieldCreateRequest model = new CustomMetaDataFieldCreateRequest - { - name = "Tst3", - label = "Test3" - }; - CustomMetaDataFieldSchemaObject schema = new CustomMetaDataFieldSchemaObject - { - type = "Number", - minValue = 1000, - maxValue = 3000, - minLength = 500, - maxLength = 600, - isValueRequired = false - }; - - model.schema = schema; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.CreateCustomMetaDataFieldsAsync(model).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void CreateCustomMetaDataFields_Type_Text() - { - CustomMetaDataFieldCreateRequest model = new CustomMetaDataFieldCreateRequest - { - name = "Tst3", - label = "Test3" - }; - CustomMetaDataFieldSchemaObject schema = new CustomMetaDataFieldSchemaObject - { - type = "Number", - minValue = 1000, - maxValue = 3000 - - }; - - model.schema = schema; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.CreateCustomMetaDataFieldsAsync(model).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void CreateCustomMetaDataFields_Type_TextArea() - { - - CustomMetaDataFieldCreateRequest model = new CustomMetaDataFieldCreateRequest - { - name = "Tst3", - label = "Test3" - }; - CustomMetaDataFieldSchemaObject schema = new CustomMetaDataFieldSchemaObject - { - type = "Number", - minValue = 1000, - maxValue = 3000 - - }; - - model.schema = schema; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.CreateCustomMetaDataFieldsAsync(model).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void CreateCustomMetaDataFields_successExpected_type_SingleSelect() - { - - List objectList = new List - { - "small", - "medium", - "large", - 30, - 40, - true - }; - - CustomMetaDataFieldCreateRequest model = new CustomMetaDataFieldCreateRequest - { - name = "Tst3", - label = "Test3" - }; - CustomMetaDataFieldSchemaObject schema = new CustomMetaDataFieldSchemaObject - { - type = "Number", - minValue = 1000, - maxValue = 3000 - }; - - model.schema = schema; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.CreateCustomMetaDataFieldsAsync(model).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void CreateCustomMetaDataFields_successExpected_type_MultiSelect() - { - CustomMetaDataFieldCreateRequest model = new CustomMetaDataFieldCreateRequest - { - name = "Tst3", - label = "Test3" - }; - List objectList = new List - { - "small", - "medium", - "large", - 30, - 40, - true - }; - - CustomMetaDataFieldCreateRequest customMetaDataFieldSchemaObject = new CustomMetaDataFieldCreateRequest - { - name = "Tst3", - label = "Test3" - }; - CustomMetaDataFieldSchemaObject schema = new CustomMetaDataFieldSchemaObject - { - type = "Number", - minValue = 1000, - maxValue = 3000 - }; - - model.schema = schema; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.CreateCustomMetaDataFieldsAsync(model).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void CreateCustomMetaDataFieldsException() - { - CustomMetaDataFieldCreateRequest model = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.CreateCustomMetaDataFieldsAsync(model)); - Assert.Equal(ErrorMessages.InvalidMetaTagValue, ex.Result.Message); - } - [Fact] - public void Missing_Name_CustomMetaDataFieldsException() - { - CustomMetaDataFieldCreateRequest model = new CustomMetaDataFieldCreateRequest - { - name = string.Empty - }; - CustomMetaDataFieldSchemaObject ob = new CustomMetaDataFieldSchemaObject - { - maxValue = 1000 - }; - model.schema = ob; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.CreateCustomMetaDataFieldsAsync(model)); - Assert.Equal(ErrorMessages.InvalidMetaTagNameValue, ex.Result.Message); - } - [Fact] - public void Missing_Label_CustomMetaDataFieldsException() - { - CustomMetaDataFieldCreateRequest model = new CustomMetaDataFieldCreateRequest - { - name = "abc", - label = string.Empty - }; - CustomMetaDataFieldSchemaObject ob = new CustomMetaDataFieldSchemaObject - { - maxValue = 1000 - }; - model.schema = ob; - - - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.CreateCustomMetaDataFieldsAsync(model)); - Assert.Equal(ErrorMessages.InvalidMetaTagLabelValue, ex.Result.Message); - } - [Fact] - public void Missing_Schema_CustomMetaDataFieldsException() - { - CustomMetaDataFieldCreateRequest model = new CustomMetaDataFieldCreateRequest - { - name = "abc", - label = "test", - schema = null - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.CreateCustomMetaDataFieldsAsync(model)); - Assert.Equal(ErrorMessages.InvalidMetaTagSchemaValue, ex.Result.Message); - } - - [Fact] - public void DeleteCustomMetaDataField_Default() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.DeleteCustomMetaDataFieldAsync("abc").Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - - [Fact] - public void Missing_fileId_CustomMetaDataFieldException() - { - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.DeleteCustomMetaDataFieldAsync("")); - Assert.Equal(ErrorMessages.InvalidfileIdsValue, ex.Result.Message); - } - - - - [Fact] - public void UpdateCustomMetaDataFields_Default() - { - CustomMetaDataFieldUpdateRequest model = new CustomMetaDataFieldUpdateRequest - { - Id = "Tst3" - }; - - CustomMetaDataFieldSchemaObject schema = new CustomMetaDataFieldSchemaObject - { - type = "Number", - minValue = 1000, - maxValue = 3000 - }; - model.schema = schema; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.UpdateCustomMetaDataFieldsAsync(model).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void Missing_Object_CustomMetaDataFieldsException() - { - CustomMetaDataFieldUpdateRequest model = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.UpdateCustomMetaDataFieldsAsync(model)); - Assert.Equal(ErrorMessages.InvalidMetaTagValue, ex.Result.Message); - } - - [Fact] - public void Missing_ID_CustomUpdateMetaDataFieldsException() - { - CustomMetaDataFieldUpdateRequest model = new CustomMetaDataFieldUpdateRequest - { - Id = "" - }; - CustomMetaDataFieldSchemaObject ob = new CustomMetaDataFieldSchemaObject - { - maxValue = 1000 - }; - model.schema = ob; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.UpdateCustomMetaDataFieldsAsync(model)); - Assert.Equal(ErrorMessages.InvalidMetaTagIdValue, ex.Result.Message); - } - [Fact] - public void Missing_Schema_CustomUpdateMetaDataFieldsException() - { - CustomMetaDataFieldUpdateRequest model = new CustomMetaDataFieldUpdateRequest - { - Id = "abc", - schema = null - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.UpdateCustomMetaDataFieldsAsync(model)); - Assert.Equal(ErrorMessages.InvalidMetaTagSchemaValue, ex.Result.Message); - } - - } -} - - - - diff --git a/Imagekit.UnitTests/MetaData/MetaDataTestNonAsync.cs b/Imagekit.UnitTests/MetaData/MetaDataTestNonAsync.cs deleted file mode 100644 index b9d0ef24..00000000 --- a/Imagekit.UnitTests/MetaData/MetaDataTestNonAsync.cs +++ /dev/null @@ -1,546 +0,0 @@ -using Imagekit.Constant; -using Imagekit.Models; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using Xunit; -using static Imagekit.Models.CustomMetaDataFieldSchemaObject; -using Imagekit.Models.Response; -using Newtonsoft.Json.Linq; - -namespace Imagekit.UnitTests.MetaData -{ - - public class MetaDataTestNonAsync - { - private const string GOOD_PUBLICKEY = "abc"; - private const string GOOD_URLENDPOINT = "https://dasdsad.dad.io/"; - - - - [Fact] - public void GetFileMetadata_DefaultNonAsync() - { - - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (ResultMetaData)restClient.GetFileMetaData("abc"); - var responseObj1 = JsonConvert.SerializeObject(responseObj.Raw); - responseObj1 = JToken.Parse(responseObj1).ToString(); - - Assert.Equal(responseObj.Raw, responseObj1); - } - [Fact] - public void GetFileMetadataExceptionNonAsync() - { - List ob = new List(); - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.GetFileMetaData("")); - Assert.Equal(ErrorMessages.FileIdMissing, ex.Message); - } - - - [Fact] - public void GetRemoteFileMetadata_DefaultNonAsync() - { - - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (ResultMetaData)restClient.GetRemoteFileMetaData("abc"); - var responseObj1 = JsonConvert.SerializeObject(responseObj.Raw); - responseObj1 = JToken.Parse(responseObj1).ToString(); - - Assert.Equal(responseObj.Raw, responseObj1); - } - [Fact] - public void GetRemoteFileMetadataExceptionNonAsync() - { - List ob = new List(); - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.GetRemoteFileMetaData("")); - Assert.Equal(ErrorMessages.InvalidUrlValue, ex.Message); - } - - - [Fact] - public void GetCustomMetaDataFields_DefaultNonAsync() - { - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.GetCustomMetaDataFields(true); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void CreateCustomMetaDataFields_DefaultNonAsync() - { - CustomMetaDataFieldCreateRequest model = new CustomMetaDataFieldCreateRequest - { - name = "Tst3", - label = "Test3" - }; - CustomMetaDataFieldSchemaObject schema = new CustomMetaDataFieldSchemaObject - { - type = "Number", - minValue = 1000, - maxValue = 3000, - minLength = 500, - maxLength = 600, - isValueRequired = false - }; - - model.schema = schema; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.CreateCustomMetaDataFields(model); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void CreateCustomMetaDataFields_Type_Date() - { - CustomMetaDataFieldCreateRequest model = new CustomMetaDataFieldCreateRequest - { - name = "Tst3", - label = "Test3" - }; - CustomMetaDataFieldSchemaObject schema = new CustomMetaDataFieldSchemaObject - { - type = "Number", - minValue = 1000, - maxValue = 3000, - minLength = 500, - maxLength = 600, - isValueRequired = false - }; - - model.schema = schema; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.CreateCustomMetaDataFields(model); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void CreateCustomMetaDataFields_Type_Text() - { - CustomMetaDataFieldCreateRequest model = new CustomMetaDataFieldCreateRequest - { - name = "Tst3", - label = "Test3" - }; - CustomMetaDataFieldSchemaObject schema = new CustomMetaDataFieldSchemaObject - { - type = "Number", - minValue = 1000, - maxValue = 3000, - minLength = 500, - maxLength = 600, - isValueRequired = false - }; - - model.schema = schema; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.CreateCustomMetaDataFields(model); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void CreateCustomMetaDataFields_Type_TextArea() - { - CustomMetaDataFieldCreateRequest model = new CustomMetaDataFieldCreateRequest - { - name = "Tst3", - label = "Test3" - }; - CustomMetaDataFieldSchemaObject schema = new CustomMetaDataFieldSchemaObject - { - type = "Number", - minValue = 1000, - maxValue = 3000, - minLength = 500, - maxLength = 600, - isValueRequired = false - }; - - model.schema = schema; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.CreateCustomMetaDataFields(model); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void CreateCustomMetaDataFields_successExpected_type_SingleSelect() - { - CustomMetaDataFieldCreateRequest model = new CustomMetaDataFieldCreateRequest - { - name = "Tst3", - label = "Test3" - }; - CustomMetaDataFieldSchemaObject schema = new CustomMetaDataFieldSchemaObject - { - type = "Number", - minValue = 1000, - maxValue = 3000, - minLength = 500, - maxLength = 600, - isValueRequired = false - }; - - model.schema = schema; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (ResultCustomMetaDataField)restClient.CreateCustomMetaDataFields(model); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void CreateCustomMetaDataFields_successExpected_type_MultiSelect() - { - CustomMetaDataFieldCreateRequest model = new CustomMetaDataFieldCreateRequest - { - name = "Tst3", - label = "Test3" - }; - - CustomMetaDataFieldSchemaObject customMetaDataFieldSchemaObject = new CustomMetaDataFieldSchemaObject - { - type = "MultiSelect", - selectOptions = new string[] { "small", "medium", "large" } - }; - - CustomMetaDataFieldSchemaObject schema = new CustomMetaDataFieldSchemaObject - { - type = "Number", - minValue = 1000, - maxValue = 3000, - minLength = 500, - maxLength = 600, - isValueRequired = false - }; - - model.schema = schema; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (ResultCustomMetaDataField)restClient.CreateCustomMetaDataFields(model); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void CreateCustomMetaDataFieldsExceptionNonAsync() - { - CustomMetaDataFieldCreateRequest model = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.CreateCustomMetaDataFields(model)); - Assert.Equal(ErrorMessages.InvalidMetaTagValue, ex.Message); - } - [Fact] - public void Missing_Name_CustomMetaDataFieldsExceptionNonAsync() - { - CustomMetaDataFieldCreateRequest model = new CustomMetaDataFieldCreateRequest - { - name = string.Empty - }; - CustomMetaDataFieldSchemaObject ob = new CustomMetaDataFieldSchemaObject - { - maxValue = 1000 - }; - model.schema = ob; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.CreateCustomMetaDataFields(model)); - Assert.Equal(ErrorMessages.InvalidMetaTagNameValue, ex.Message); - } - [Fact] - public void Missing_Label_CustomMetaDataFieldsExceptionNonAsync() - { - CustomMetaDataFieldCreateRequest model = new CustomMetaDataFieldCreateRequest - { - name = "abc", - label = string.Empty - }; - CustomMetaDataFieldSchemaObject ob = new CustomMetaDataFieldSchemaObject - { - maxValue = 1000 - }; - model.schema = ob; - - - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.CreateCustomMetaDataFields(model)); - Assert.Equal(ErrorMessages.InvalidMetaTagLabelValue, ex.Message); - } - [Fact] - public void Missing_Schema_CustomMetaDataFieldsExceptionNonAsync() - { - CustomMetaDataFieldCreateRequest model = new CustomMetaDataFieldCreateRequest - { - name = "abc", - label = "test", - schema = null - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.CreateCustomMetaDataFields(model)); - Assert.Equal(ErrorMessages.InvalidMetaTagSchemaValue, ex.Message); - } - - [Fact] - public void DeleteCustomMetaDataField_DefaultNonAsync() - { - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.DeleteCustomMetaDataField("abc"); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - - [Fact] - public void Missing_fileId_CustomMetaDataFieldExceptionNonAsync() - { - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.DeleteCustomMetaDataField("")); - Assert.Equal(ErrorMessages.InvalidfileIdsValue, ex.Message); - } - - - - [Fact] - public void UpdateCustomMetaDataFields_DefaultNonAsync() - { - CustomMetaDataFieldUpdateRequest model = new CustomMetaDataFieldUpdateRequest - { - Id = "Tst3" - }; - - CustomMetaDataFieldSchemaObject schema = new CustomMetaDataFieldSchemaObject - { - type = "Number", - minValue = 1000, - maxValue = 3000 - }; - model.schema = schema; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.UpdateCustomMetaDataFields(model); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void Missing_Object_CustomMetaDataFieldsExceptionNonAsync() - { - CustomMetaDataFieldUpdateRequest model = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.UpdateCustomMetaDataFields(model)); - Assert.Equal(ErrorMessages.InvalidMetaTagValue, ex.Message); - } - - [Fact] - public void Missing_ID_CustomUpdateMetaDataFieldsExceptionNonAsync() - { - CustomMetaDataFieldUpdateRequest model = new CustomMetaDataFieldUpdateRequest - { - Id = "" - }; - CustomMetaDataFieldSchemaObject ob = new CustomMetaDataFieldSchemaObject - { - maxValue = 1000 - }; - model.schema = ob; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.UpdateCustomMetaDataFields(model)); - Assert.Equal(ErrorMessages.InvalidMetaTagIdValue, ex.Message); - } - [Fact] - public void Missing_Schema_CustomUpdateMetaDataFieldsExceptionNonAsync() - { - CustomMetaDataFieldUpdateRequest model = new CustomMetaDataFieldUpdateRequest - { - Id = "abc", - schema = null - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.UpdateCustomMetaDataFields(model)); - Assert.Equal(ErrorMessages.InvalidMetaTagSchemaValue, ex.Message); - } - - } -} - - - - diff --git a/Imagekit.UnitTests/Sample/test.jpg b/Imagekit.UnitTests/Sample/test.jpg deleted file mode 100644 index 79de088c..00000000 Binary files a/Imagekit.UnitTests/Sample/test.jpg and /dev/null differ diff --git a/Imagekit.UnitTests/Tags/ManageTagsTestAsync.cs b/Imagekit.UnitTests/Tags/ManageTagsTestAsync.cs deleted file mode 100644 index 87d4a1bb..00000000 --- a/Imagekit.UnitTests/Tags/ManageTagsTestAsync.cs +++ /dev/null @@ -1,240 +0,0 @@ -using Imagekit.Constant; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using Xunit; - -namespace Imagekit.UnitTests.Tags -{ - - public class ManageTagsTestAsync - { - private const string GOOD_PUBLICKEY = "abc"; - private const string GOOD_URLENDPOINT = "https://dasdsad.dad.io/"; - - - [Fact] - public void AddTags_Default() - { - TagsRequest tagsRequest = new TagsRequest - { - tags = new List - { - "abc", - "abc" - }, - fileIds = new List - { - "abc" - } - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.ManageTagsAsync(tagsRequest, "addTags").Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void AddTags_Null_Exception() - { - TagsRequest ob = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.ManageTagsAsync(ob, "")); - Assert.Equal(ErrorMessages.InvalidTagValue, ex.Result.Message); - } - [Fact] - public void Missing_Tags_Null_Exception() - { - TagsRequest ob = new TagsRequest - { - tags = null, - fileIds = new List { "abc" } - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.ManageTagsAsync(ob, "")); - Assert.Equal(ErrorMessages.InvalidTagParamValue, ex.Result.Message); - } - - [Fact] - public void Missing_Filed_Null_Exception() - { - TagsRequest ob = new TagsRequest - { - tags = new List { "abc" }, - fileIds = null - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.ManageTagsAsync(ob, "")); - Assert.Equal(ErrorMessages.InvalidFiledParamValue, ex.Result.Message); - } - - - [Fact] - public void RemoveTags_Default() - { - TagsRequest tagsRequest = new TagsRequest - { - tags = new List - { - "abc", - "abc" - }, - fileIds = new List - { - "abc" - } - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.ManageTagsAsync(tagsRequest, "addTags").Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void Remove_Tags_Object_Exception() - { - TagsRequest ob = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.ManageTagsAsync(ob, "")); - Assert.Equal(ErrorMessages.InvalidTagValue, ex.Result.Message); - } - [Fact] - public void Missing_Remove_Tags_Null_Exception() - { - TagsRequest ob = new TagsRequest - { - tags = null, - fileIds = new List { "abc" } - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.ManageTagsAsync(ob, "")); - Assert.Equal(ErrorMessages.InvalidTagParamValue, ex.Result.Message); - } - - - [Fact] - public void Remove_AITags_Null_Exception() - { - AITagsRequest ob = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.RemoveAITagsAsync(ob)); - Assert.Equal(ErrorMessages.InvalidTagValue, ex.Result.Message); - } - [Fact] - public void Missing_AI_Tags_Null_Exception() - { - AITagsRequest ob = new AITagsRequest - { - AITags = null, - fileIds = new List { "abc" } - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.RemoveAITagsAsync(ob)); - Assert.Equal(ErrorMessages.InvalidTagParamValue, ex.Result.Message); - } - - [Fact] - public void Missing_AI_Filed_Null_Exception() - { - AITagsRequest ob = new AITagsRequest - { - AITags = new List { "abc" }, - fileIds = null - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.RemoveAITagsAsync(ob)); - Assert.Equal(ErrorMessages.InvalidFiledParamValue, ex.Result.Message); - } - - } -} - - - - diff --git a/Imagekit.UnitTests/Tags/ManageTagsTestNonAsync.cs b/Imagekit.UnitTests/Tags/ManageTagsTestNonAsync.cs deleted file mode 100644 index fca2fdea..00000000 --- a/Imagekit.UnitTests/Tags/ManageTagsTestNonAsync.cs +++ /dev/null @@ -1,240 +0,0 @@ -using Imagekit.Constant; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using Xunit; - -namespace Imagekit.UnitTests.Tags -{ - - public class ManageTagsTestNonAsync - { - private const string GOOD_PUBLICKEY = "abc"; - private const string GOOD_URLENDPOINT = "https://dasdsad.dad.io/"; - - - [Fact] - public void AddTags_DefaultNonAsync() - { - TagsRequest tagsRequest = new TagsRequest - { - tags = new List - { - "abc", - "abc" - }, - fileIds = new List - { - "abc" - } - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.ManageTags(tagsRequest, "addTags"); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void AddTags_Null_ExceptionNonAsync() - { - TagsRequest ob = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.ManageTags(ob, "")); - Assert.Equal(ErrorMessages.InvalidTagValue, ex.Message); - } - [Fact] - public void Missing_Tags_Null_ExceptionNonAsync() - { - TagsRequest ob = new TagsRequest - { - tags = null, - fileIds = new List { "abc" } - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.ManageTags(ob, "")); - Assert.Equal(ErrorMessages.InvalidTagParamValue, ex.Message); - } - - [Fact] - public void Missing_Filed_Null_ExceptionNonAsync() - { - TagsRequest ob = new TagsRequest - { - tags = new List { "abc" }, - fileIds = null - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.ManageTags(ob, "")); - Assert.Equal(ErrorMessages.InvalidFiledParamValue, ex.Message); - } - - - [Fact] - public void RemoveTags_DefaultNonAsync() - { - TagsRequest tagsRequest = new TagsRequest - { - tags = new List - { - "abc", - "abc" - }, - fileIds = new List - { - "abc" - } - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = restClient.ManageTags(tagsRequest, "removeTags"); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void Remove_Tags_Object_ExceptionNonAsync() - { - TagsRequest ob = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.ManageTags(ob, "")); - Assert.Equal(ErrorMessages.InvalidTagValue, ex.Message); - } - [Fact] - public void Missing_Remove_Tags_Null_ExceptionNonAsync() - { - TagsRequest ob = new TagsRequest - { - tags = null, - fileIds = new List { "abc" } - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.ManageTags(ob, "")); - Assert.Equal(ErrorMessages.InvalidTagParamValue, ex.Message); - } - - - [Fact] - public void Remove_AITags_Null_ExceptionNonAsync() - { - AITagsRequest ob = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.RemoveAITags(ob)); - Assert.Equal(ErrorMessages.InvalidTagValue, ex.Message); - } - [Fact] - public void Missing_AI_Tags_Null_ExceptionNonAsync() - { - AITagsRequest ob = new AITagsRequest - { - AITags = null, - fileIds = new List { "abc" } - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.RemoveAITags(ob)); - Assert.Equal(ErrorMessages.InvalidTagParamValue, ex.Message); - } - - [Fact] - public void Missing_AI_Filed_Null_ExceptionNonAsync() - { - AITagsRequest ob = new AITagsRequest - { - AITags = new List { "abc" }, - fileIds = null - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.RemoveAITags(ob)); - Assert.Equal(ErrorMessages.InvalidFiledParamValue, ex.Message); - } - - } -} - - - - diff --git a/Imagekit.UnitTests/TestHelpers.cs b/Imagekit.UnitTests/TestHelpers.cs deleted file mode 100644 index 8a7aef75..00000000 --- a/Imagekit.UnitTests/TestHelpers.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Bogus; -using Moq; -using Moq.Protected; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Xunit; - -namespace Imagekit.UnitTests -{ - public static class TestHelpers - { - static TestHelpers() - { - Faker.DefaultStrictMode = true; - } - - public static Faker ImagekitResponseFaker = new Faker() - .RuleFor(u => u.Raw, (f, u) => f.Random.Utf16String()) - .RuleFor(u => u.HttpStatusCode, (f, u) => 200); - - /// - /// Get a test http client that response to the specified request. - /// - /// An expression that matches the sent in the request. - /// The response to return from the request. - /// The function to call with the sent in the request. - /// - public static HttpClient GetTestHttpClient(Expression requestMatcher, HttpResponseMessage response, Action callback) - { - var handlerMock = new Mock(MockBehavior.Strict); - handlerMock - .Protected() - // Setup the PROTECTED method to mock - .Setup>( - "SendAsync", - requestMatcher, - ItExpr.IsAny() - ) - .Callback((msg, ct) => callback?.Invoke(msg)) - // prepare the expected response of the mocked http call - .ReturnsAsync(response) - .Verifiable(); - - // use real http client with mocked handler here - var httpClient = new HttpClient(handlerMock.Object); - return httpClient; - } - - - /// - /// Get a test http client that response to the specified request. - /// - /// The response to return from the request. - /// The function to call with the sent in the request. - /// - public static HttpClient GetTestHttpClient(HttpResponseMessage response) - { - return GetTestHttpClient(ItExpr.IsAny(), response, null); - } - - - public static Action GetUpdateFileDetailsMessageValidator( - string[] expectedTags, - string expectedCustomCoordinates - ) - { - return async (msg) => - { - var content = await msg.Content.ReadAsStringAsync(); - dynamic jobj = JObject.Parse(content); - string[] actualTags = jobj.tags == null ? null : JsonConvert.DeserializeObject(jobj.tags.ToString()); - string coords = jobj.customCoordinates; - Assert.Equal(expectedTags, actualTags); - Assert.Equal(expectedCustomCoordinates, coords); - }; - } - - } -} diff --git a/Imagekit.UnitTests/Upload/UploadTestCasesAsync.cs b/Imagekit.UnitTests/Upload/UploadTestCasesAsync.cs deleted file mode 100644 index 2a249062..00000000 --- a/Imagekit.UnitTests/Upload/UploadTestCasesAsync.cs +++ /dev/null @@ -1,431 +0,0 @@ -using Imagekit.Constant; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using Xunit; -using Imagekit.Models; -using System.Collections; - -namespace Imagekit.UnitTests.Upload -{ - - public class UploadTestCasesAsync - { - private const string GOOD_PUBLICKEY = "abc"; - private const string GOOD_URLENDPOINT = "https://dasdsad.dad.io/"; - - - [Fact] - public void Upload_InvalidObject_Exception() - { - - FileCreateRequest ob = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.UploadAsync(ob)); - Assert.Equal(ErrorMessages.InvalidFileUploadObjValue, ex.Result.Message); - } - [Fact] - public void Upload_InvalidFileName_Exception() - { - FileCreateRequest ob = new FileCreateRequest - { - fileName = string.Empty - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.UploadAsync(ob)); - Assert.Equal(ErrorMessages.MissingUploadFilenameParameter, ex.Result.Message); - } - [Fact] - public void Upload_InvalidFileParam_Exception() - { - - FileCreateRequest ob = new FileCreateRequest - { - file = null, - fileName = Guid.NewGuid().ToString() - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.ThrowsAsync(async () => await restClient.UploadAsync(ob)); - Assert.Equal(ErrorMessages.InvalidFileValue, ex.Result.Message); - } - - [Fact] - public void UploadFileByURI_Default() - { - FileCreateRequest ob = new FileCreateRequest - { - file = "http://www.google.com/images/logos/ps_logo2.png", - fileName = Guid.NewGuid().ToString() - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var response = (Result)restClient.Upload(ob); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void UploadFileByBytes_Default() - { - - string base64 = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; - byte[] bytes = Convert.FromBase64String(base64); - - FileCreateRequest ob = new FileCreateRequest - { - file = bytes, - fileName = Guid.NewGuid().ToString() - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - - - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (Result)restClient.UploadAsync(ob).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void UploadFileByBase64_Default() - { - string base64 = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; - FileCreateRequest ob = new FileCreateRequest - { - file = base64, - fileName = Guid.NewGuid().ToString() - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - - - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (Result)restClient.UploadAsync(ob).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void UploadFile_Default() - { - string base64 = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; - FileCreateRequest ob = new FileCreateRequest - { - file = base64, - fileName = Guid.NewGuid().ToString() - }; - List tags = new List - { - "Software", - "Developer", - "Engineer" - }; - ob.tags = tags; - - string customCoordinates = "10,10,20,20"; - ob.customCoordinates = customCoordinates; - List responseFields = new List - { - "isPrivateFile", - "tags", - "customCoordinates" - }; - - ob.responseFields = responseFields; - List model1 = new List(); - BackGroundImage bck = new BackGroundImage - { - name = "remove-bg", - options = new options() { add_shadow = true, bg_color = "green" } - }; - TransformationObject transformationObject = new TransformationObject - { - value = "w-100" - }; - List postTransformations = new List(); - postTransformations.Add(transformationObject); - UploadTransformation uploadTransformation = new UploadTransformation - { - pre = "l-text,i-Imagekit,fs-50,l-end", - post = postTransformations, - }; - model1.Add(bck); - ob.webhookUrl = "https://webhook.site/c78d617f-33bc-40d9-9e61-608999721e2e"; - ob.useUniqueFileName = true; - ob.folder = "dummy-folder"; - ob.isPrivateFile = false; - ob.overwriteFile = true; - ob.overwriteAITags = true; - ob.overwriteTags = true; - ob.overwriteCustomMetadata = true; - ob.transformation = uploadTransformation; - ob.checks = "'request.folder' : '/dummy-folder'"; - ob.isPublished = true; - Hashtable model = new Hashtable - { - { "price", 2000 } - }; - ob.customMetadata = model; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (Result)restClient.UploadAsync(ob).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void UploadFile_Null_List() - { - string base64 = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; - FileCreateRequest ob = new FileCreateRequest - { - file = base64, - fileName = Guid.NewGuid().ToString() - }; - List tags = null; - ob.tags = tags; - ob.folder = "demo1"; - string customCoordinates = "10,10,20,20"; - ob.customCoordinates = customCoordinates; - List responseFields = null; - ob.responseFields = responseFields; - List model1 = new List(); - BackGroundImage bck = new BackGroundImage - { - name = "remove-bg", - options = new options() { add_shadow = true, bg_color = "green", bg_image_url = "http://www.google.com/images/logos/ps_logo2.png" } - }; - model1.Add(bck); - ob.webhookUrl = "https://webhook.site/c78d617f-33bc-40d9-9e61-608999721e2e"; - ob.useUniqueFileName = false; - ob.isPrivateFile = false; - ob.overwriteFile = false; - ob.overwriteAITags = false; - ob.overwriteTags = false; - ob.overwriteCustomMetadata = true; - Hashtable model = new Hashtable - { - { "price", 2000 } - }; - ob.customMetadata = model; - - - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - - - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (Result)restClient.UploadAsync(ob).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - - [Fact] - public void UploadFile_Null_Object_List() - { - string base64 = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; - FileCreateRequest ob = new FileCreateRequest - { - file = base64, - fileName = Guid.NewGuid().ToString() - }; - List tags = null; - ob.tags = tags; - ob.folder = "demo1"; - string customCoordinates = "10,10,20,20"; - ob.customCoordinates = customCoordinates; - List responseFields = null; - ob.responseFields = responseFields; - Hashtable model = new Hashtable - { - { "price", 2000 } - }; - ob.customMetadata = model; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - - - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (Result)restClient.UploadAsync(ob).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void UpdateFile_Missing_File_Id() - { - FileUpdateRequest ob = new FileUpdateRequest - { - fileId = "", - - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var ex = Assert.ThrowsAsync(async () => await restClient.UpdateFileDetailAsync(ob)); - Assert.Equal(ErrorMessages.FileIdMissing, ex.Result.Message); - } - - [Fact] - public void UpdateFile_Default() - { - FileUpdateRequest ob = new FileUpdateRequest - { - fileId = "file-Id", - - }; - List tags = new List - { - "Software", - "Developer", - "Engineer" - }; - ob.tags = tags; - - string customCoordinates = "10,10,20,20"; - ob.customCoordinates = customCoordinates; - List responseFields = new List - { - "isPrivateFile", - "tags", - "customCoordinates" - }; - List model1 = new List(); - BackGroundImage bck = new BackGroundImage - { - name = "remove-bg", - options = new options() { add_shadow = true, bg_color = "green", bg_image_url = "http://www.google.com/images/logos/ps_logo2.png" } - }; - model1.Add(bck); - ob.webhookUrl = "https://webhook.site/c78d617f-33bc-40d9-9e61-608999721e2e"; - Hashtable model = new Hashtable - { - { "price", 2000 } - }; - ob.customMetadata = model; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (Result)restClient.UpdateFileDetailAsync(ob).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void UpdateFile_Publish_Status() - { - FileUpdateRequest ob = new FileUpdateRequest - { - fileId = "file-Id", - - }; - PublishStatus publishStatus = new PublishStatus - { - isPublished = false - }; - ob.publish = publishStatus; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (Result)restClient.UpdateFileDetailAsync(ob).Result; - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - - } -} - - - - diff --git a/Imagekit.UnitTests/Upload/UploadTestCasesNonAsync.cs b/Imagekit.UnitTests/Upload/UploadTestCasesNonAsync.cs deleted file mode 100644 index 4ed8340f..00000000 --- a/Imagekit.UnitTests/Upload/UploadTestCasesNonAsync.cs +++ /dev/null @@ -1,421 +0,0 @@ -using Imagekit.Constant; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using Xunit; -using Imagekit.Models; -using System.Collections; - -namespace Imagekit.UnitTests.Upload -{ - - public class UploadTestCasesNonAsync - { - private const string GOOD_PUBLICKEY = "abc"; - private const string GOOD_URLENDPOINT = "https://dasdsad.dad.io/"; - - - [Fact] - public void Upload_InvalidObject_ExceptionNonAsync() - { - - FileCreateRequest ob = null; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.Upload(ob)); - Assert.Equal(ErrorMessages.InvalidFileUploadObjValue, ex.Message); - } - [Fact] - public void Upload_InvalidFileName_ExceptionNonAsync() - { - FileCreateRequest ob = new FileCreateRequest - { - fileName = string.Empty - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.Upload(ob)); - Assert.Equal(ErrorMessages.MissingUploadFilenameParameter, ex.Message); - } - [Fact] - public void Upload_InvalidFileParam_ExceptionNonAsync() - { - - FileCreateRequest ob = new FileCreateRequest - { - file = null, - fileName = Guid.NewGuid().ToString() - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var ex = Assert.Throws(() => restClient.Upload(ob)); - Assert.Equal(ErrorMessages.InvalidFileValue, ex.Message); - } - - [Fact] - public void UploadFileByURI_DefaultNonAsync() - { - FileCreateRequest ob = new FileCreateRequest - { - file = "http://www.google.com/images/logos/ps_logo2.png", - fileName = Guid.NewGuid().ToString() - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - var response = (Result)restClient.Upload(ob); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void UploadFileByBytes_DefaultNonAsync() - { - - string base64 = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; - byte[] bytes = Convert.FromBase64String(base64); - - FileCreateRequest ob = new FileCreateRequest - { - file = bytes, - fileName = Guid.NewGuid().ToString() - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - - - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (Result)restClient.Upload(ob); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void UploadFileByBase64_DefaultNonAsync() - { - string base64 = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; - FileCreateRequest ob = new FileCreateRequest - { - file = base64, - fileName = Guid.NewGuid().ToString() - }; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - - - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (Result)restClient.Upload(ob); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void UploadFile_DefaultNonAsync() - { - string base64 = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; - FileCreateRequest ob = new FileCreateRequest - { - file = base64, - fileName = Guid.NewGuid().ToString() - }; - List tags = new List - { - "Software", - "Developer", - "Engineer" - }; - ob.tags = tags; - ob.folder = "demo1"; - string customCoordinates = "10,10,20,20"; - ob.customCoordinates = customCoordinates; - List responseFields = new List - { - "isPrivateFile", - "tags", - "customCoordinates" - }; - - ob.responseFields = responseFields; - - List model1 = new List(); - BackGroundImage bck = new BackGroundImage - { - name = "remove-bg", - options = new options() { add_shadow = true, bg_image_url = "http://www.google.com/images/logos/ps_logo2.png" } - }; - model1.Add(bck); - ob.extensions = model1; - ob.webhookUrl = "https://webhook.site/c78d617f-33bc-40d9-9e61-608999721e2e"; - ob.useUniqueFileName = false; - ob.isPrivateFile = false; - ob.overwriteFile = false; - ob.overwriteAITags = false; - ob.overwriteTags = false; - ob.overwriteCustomMetadata = true; - ob.checks = "'request.folder' : '/dummy-folder'"; - ob.isPublished = true; - Hashtable model = new Hashtable - { - { "price", 2000 } - }; - ob.customMetadata = model; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - - - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (Result)restClient.Upload(ob); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void UploadFile_Null_List() - { - string base64 = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; - FileCreateRequest ob = new FileCreateRequest - { - file = base64, - fileName = Guid.NewGuid().ToString() - }; - List tags = null; - ob.tags = tags; - ob.folder = "demo1"; - string customCoordinates = "10,10,20,20"; - ob.customCoordinates = customCoordinates; - List responseFields = null; - - ob.responseFields = responseFields; - List model1 = new List(); - BackGroundImage bck = new BackGroundImage - { - name = "remove-bg", - options = new options() { add_shadow = true, bg_image_url = "http://www.google.com/images/logos/ps_logo2.png" } - }; - model1.Add(bck); - ob.webhookUrl = "https://webhook.site/c78d617f-33bc-40d9-9e61-608999721e2e"; - ob.useUniqueFileName = false; - ob.isPrivateFile = false; - ob.overwriteFile = false; - ob.overwriteAITags = false; - ob.overwriteTags = false; - ob.overwriteCustomMetadata = true; - Hashtable model = new Hashtable - { - { "price", 2000 } - }; - ob.customMetadata = model; - - - - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - - - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (Result)restClient.Upload(ob); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - - [Fact] - public void UploadFile_Null_Object_List() - { - string base64 = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; - FileCreateRequest ob = new FileCreateRequest - { - file = base64, - fileName = Guid.NewGuid().ToString() - }; - List tags = null; - ob.tags = tags; - ob.folder = "demo1"; - string customCoordinates = "10,10,20,20"; - ob.customCoordinates = customCoordinates; - List responseFields = null; - ob.responseFields = responseFields; - Hashtable model = new Hashtable - { - { "price", 2000 } - }; - ob.customMetadata = model; - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - - - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (Result)restClient.Upload(ob); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - [Fact] - public void UpdateFile_Missing_File_Id() - { - FileUpdateRequest ob = new FileUpdateRequest - { - fileId = "", - - }; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var ex = Assert.ThrowsAsync(async () => await restClient.UpdateFileDetailAsync(ob)); - Assert.Equal(ErrorMessages.FileIdMissing, ex.Result.Message); - } - [Fact] - public void UpdateFile_Default() - { - FileUpdateRequest ob = new FileUpdateRequest - { - fileId = "file-Id", - - }; - List tags = new List - { - "Software", - "Developer", - "Engineer" - }; - ob.tags = tags; - - string customCoordinates = "10,10,20,20"; - ob.customCoordinates = customCoordinates; - List responseFields = new List - { - "isPrivateFile", - "tags", - "customCoordinates" - }; - List model1 = new List(); - BackGroundImage bck = new BackGroundImage - { - name = "remove-bg", - options = new options() { add_shadow = true, bg_color = "green" } - }; - model1.Add(bck); - ob.webhookUrl = "https://webhook.site/c78d617f-33bc-40d9-9e61-608999721e2e"; - Hashtable model = new Hashtable - { - { "price", 2000 } - }; - ob.customMetadata = model; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (Result)restClient.UpdateFileDetail(ob); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - [Fact] - public void UpdateFile_Publish_Status() - { - FileUpdateRequest ob = new FileUpdateRequest - { - fileId = "file-Id", - - }; - PublishStatus publishStatus = new PublishStatus - { - isPublished = false - }; - ob.publish = publishStatus; - - var responseObj = TestHelpers.ImagekitResponseFaker.Generate(); - var httpResponse = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(JsonConvert.SerializeObject(responseObj)) - }; - var httpClient = TestHelpers.GetTestHttpClient(httpResponse); - - var restClient = new RestClient(GOOD_PUBLICKEY, GOOD_URLENDPOINT, httpClient); - - var response = (Result)restClient.UpdateFileDetail(ob); - var responseObj1 = JsonConvert.SerializeObject(responseObj); - Assert.Equal(responseObj1, response.Raw); - } - - } -} - - - - diff --git a/Imagekit.crdata b/Imagekit.crdata deleted file mode 100644 index bb1963a8..00000000 --- a/Imagekit.crdata +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - AAEAAAD/////AQAAAAAAAAAMAgAAAFxTdWJNYWluLkNvZGVJdFJpZ2h0LlNkaywgVmVyc2lvbj0xLjEuMzYuMjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49MTNmODc4MmMwYzg4YjUxMAwDAAAAYFN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUsIFZlcnNpb249MS4xLjIyLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49NzliYzBkYjk3NDBhNWYwNAwEAAAAZlN1Yk1haW4uQ29kZUl0UmlnaHQuQ29kZU9iamVjdE1vZGVsLCBWZXJzaW9uPTIuMi4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49YmE5NjRiYzlhZGYxZTE4OQUBAAAALFN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLlJlZmxlY3Rpb24uQ2xhc3NJbmZvCAAAAAVfbmFtZQpfY2xhc3NUeXBlB19wYXJlbnQOX25hbWVzcGFjZUluZm8OX2Nhc2VTZW5zaXRpdmUKX21vZGlmaWVycwxfcHJvamVjdE5hbWUNX3Byb2plY3RGaWxlcwEEBAQABAEENVN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5DbGFzc0luZm9UeXBlAwAAADFTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVR5cGVJbmZvAwAAADBTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5SZWZsZWN0aW9uLk5hbWVzcGFjZUluZm8CAAAAATJTdWJNYWluLkNvZGVPYmplY3RNb2RlbC5SZWZsZWN0aW9uLk1lbWJlck1vZGlmaWVycwQAAAA6U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLklQcm9qZWN0RmlsZUluZm9bXQMAAAACAAAABgUAAAAHUHJvZ3JhbQX6////NVN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5DbGFzc0luZm9UeXBlAQAAAAd2YWx1ZV9fAAgDAAAAAAAAAAoJBwAAAAEF+P///zJTdWJNYWluLkNvZGVPYmplY3RNb2RlbC5SZWZsZWN0aW9uLk1lbWJlck1vZGlmaWVycwEAAAAHdmFsdWVfXwAIBAAAAAgAAAAGCQAAAAtDb25zb2xlQXBwMQkKAAAABQcAAAAwU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5OYW1lc3BhY2VJbmZvAgAAABNfZnVsbHlRdWFsaWZpZWROYW1lCV9wcm9qZWN0cwEENlN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5JUHJvamVjdEluZm9bXQMAAAACAAAABgsAAAALQ29uc29sZUFwcDEJDAAAAAcKAAAAAAEAAAABAAAABDhTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVByb2plY3RGaWxlSW5mbwMAAAAJDQAAAAcMAAAAAAEAAAABAAAABDRTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVByb2plY3RJbmZvAwAAAAkOAAAABQ0AAAAyU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5Qcm9qZWN0RmlsZUluZm8CAAAADF9wcm9qZWN0TmFtZRFfcmVsYXRpdmVGaWxlTmFtZQEBAgAAAAkJAAAABhAAAAAMLlxQcm9ncmFtLmNzBQ4AAAAuU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5Qcm9qZWN0SW5mbwEAAAAFX25hbWUBAgAAAAkJAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= - - - - AAEAAAD/////AQAAAAAAAAAMAgAAAFxTdWJNYWluLkNvZGVJdFJpZ2h0LlNkaywgVmVyc2lvbj0xLjEuMzYuMjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49MTNmODc4MmMwYzg4YjUxMAwDAAAAZlN1Yk1haW4uQ29kZUl0UmlnaHQuQ29kZU9iamVjdE1vZGVsLCBWZXJzaW9uPTIuMi4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49YmE5NjRiYzlhZGYxZTE4OQUBAAAAMVN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLlJlZmxlY3Rpb24uRXhwcmVzc2lvbkluZm8EAAAAD19leHByZXNzaW9uVHlwZQdfcGFyZW50DF9wcm9qZWN0RmlsZQZfaW5kZXgEAgQAMVN1Yk1haW4uQ29kZU9iamVjdE1vZGVsLlJlZmxlY3Rpb24uRXhwcmVzc2lvblR5cGUDAAAAMlN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLlJlZmxlY3Rpb24uUHJvamVjdEZpbGVJbmZvAgAAAAgCAAAABfz///8xU3ViTWFpbi5Db2RlT2JqZWN0TW9kZWwuUmVmbGVjdGlvbi5FeHByZXNzaW9uVHlwZQEAAAAHdmFsdWVfXwAIAwAAAA8AAAAJBQAAAAkGAAAAAAAAAAEFAAAAAQAAAAH5/////P///wsAAAAJCAAAAAkJAAAAAAAAAAUGAAAAMlN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLlJlZmxlY3Rpb24uUHJvamVjdEZpbGVJbmZvAgAAAAxfcHJvamVjdE5hbWURX3JlbGF0aXZlRmlsZU5hbWUBAQIAAAAGCgAAAAhJbWFnZWtpdAYLAAAADy5cUmVzdENsaWVudC5jcwEIAAAAAQAAAAH0/////P///xgAAAAJDQAAAAkOAAAAAAAAAAEJAAAABgAAAAkKAAAABhAAAAAPLlxSZXN0Q2xpZW50LmNzAQ0AAAABAAAAAe/////8////IwAAAAkSAAAACRMAAAD/////AQ4AAAAGAAAACQoAAAAGFQAAAA8uXFJlc3RDbGllbnQuY3MFEgAAAC9TdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5SZWZsZWN0aW9uLlZhcmlhYmxlSW5mbwQAAAAFX25hbWUPX3JldHVyblR5cGVJbmZvB19wYXJlbnQKX3N0YXRlbWVudAEEBAQxU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5SZXR1cm5UeXBlSW5mbwIAAAAtU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5NZXRob2RJbmZvAgAAADBTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5SZWZsZWN0aW9uLlN0YXRlbWVudEluZm8CAAAAAgAAAAYWAAAABmFwSXVybAkXAAAACRgAAAAJGQAAAAETAAAABgAAAAkKAAAABhsAAAAPLlxSZXN0Q2xpZW50LmNzDBwAAABgU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZSwgVmVyc2lvbj0xLjEuMjIuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj03OWJjMGRiOTc0MGE1ZjA0BRcAAAAxU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5SZXR1cm5UeXBlSW5mbwYAAAAFX3R5cGUUX3BvaW50ZXJOZXN0aW5nTGV2ZWwQX2FycmF5RGltZW5zaW9ucwxfcHJvamVjdEZpbGURX2dlbmVyaWNBcmd1bWVudHMTX2Z1bGx5UXVhbGlmaWVkTmFtZQQABwQEATFTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVR5cGVJbmZvHAAAAAgIMlN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLlJlZmxlY3Rpb24uUHJvamVjdEZpbGVJbmZvAgAAADlTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVJldHVyblR5cGVJbmZvW10cAAAAAgAAAAoAAAAACgkdAAAACR4AAAAGHwAAAA1TeXN0ZW0uU3RyaW5nBRgAAAAtU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5NZXRob2RJbmZvBwAAAAVfbmFtZQdfcGFyZW50C19yZXR1cm5UeXBlDF9wcm9qZWN0RmlsZQ5fY2FzZVNlbnNpdGl2ZQpfbW9kaWZpZXJzC19wYXJhbWV0ZXJzAQQEBAAEBCxTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5SZWZsZWN0aW9uLkNsYXNzSW5mbwIAAAAxU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5SZXR1cm5UeXBlSW5mbwIAAAAyU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5Qcm9qZWN0RmlsZUluZm8CAAAAATtTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uTWVtYmVySW5mb01vZGlmaWVycxwAAAA4U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLklQYXJhbWV0ZXJJbmZvW10cAAAAAgAAAAYgAAAAFWdldFJlbW90ZUZpbGVNZXRhRGF0YQkhAAAACSIAAAAJIwAAAAEF3P///ztTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uTWVtYmVySW5mb01vZGlmaWVycwEAAAAHdmFsdWVfXwAIHAAAAAIAAAAJJQAAAAUZAAAAMFN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLlJlZmxlY3Rpb24uU3RhdGVtZW50SW5mbwQAAAAYQmxvY2tJbmZvK19zdGF0ZW1lbnRUeXBlFUJsb2NrSW5mbytfbWVtYmVySW5mbxFCbG9ja0luZm8rX3BhcmVudBBCbG9ja0luZm8rX2luZGV4BAQEADBTdWJNYWluLkNvZGVPYmplY3RNb2RlbC5SZWZsZWN0aW9uLlN0YXRlbWVudFR5cGUDAAAALVN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLlJlZmxlY3Rpb24uTWV0aG9kSW5mbwIAAAA2U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLklTdGF0ZW1lbnRJbmZvHAAAAAgCAAAABdr///8wU3ViTWFpbi5Db2RlT2JqZWN0TW9kZWwuUmVmbGVjdGlvbi5TdGF0ZW1lbnRUeXBlAQAAAAd2YWx1ZV9fAAgDAAAAAwAAAAknAAAACgEAAAABHQAAAAYAAAAJCgAAAAYpAAAADy5cUmVzdENsaWVudC5jcwceAAAAAAEAAAAAAAAABDdTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVJldHVyblR5cGVJbmZvHAAAAAUhAAAALFN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLlJlZmxlY3Rpb24uQ2xhc3NJbmZvCAAAAAVfbmFtZQpfY2xhc3NUeXBlB19wYXJlbnQOX25hbWVzcGFjZUluZm8OX2Nhc2VTZW5zaXRpdmUKX21vZGlmaWVycwxfcHJvamVjdE5hbWUNX3Byb2plY3RGaWxlcwEEBAQABAEENVN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5DbGFzc0luZm9UeXBlHAAAADFTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVR5cGVJbmZvHAAAADBTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5SZWZsZWN0aW9uLk5hbWVzcGFjZUluZm8CAAAAATJTdWJNYWluLkNvZGVPYmplY3RNb2RlbC5SZWZsZWN0aW9uLk1lbWJlck1vZGlmaWVycwMAAAA6U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLklQcm9qZWN0RmlsZUluZm9bXRwAAAACAAAABioAAAAKUmVzdENsaWVudAXV////NVN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5DbGFzc0luZm9UeXBlAQAAAAd2YWx1ZV9fAAgcAAAAAAAAAAoJLAAAAAEF0////zJTdWJNYWluLkNvZGVPYmplY3RNb2RlbC5SZWZsZWN0aW9uLk1lbWJlck1vZGlmaWVycwEAAAAHdmFsdWVfXwAIAwAAAAIAAAAJCgAAAAkvAAAAASIAAAAXAAAACgAAAAAKCSMAAAAJMQAAAAYyAAAANXtBQzczREM2MS05OTkxLTRBMTUtQjg0NC0yNTMyREU0MkFCOER9LlJlc3VsdE1ldGFEYXRhASMAAAAGAAAACQoAAAAGNAAAAA8uXFJlc3RDbGllbnQuY3MHJQAAAAABAAAAAQAAAAQ2U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLklQYXJhbWV0ZXJJbmZvHAAAAAk1AAAAAScAAAAYAAAACSAAAAAJNwAAAAk4AAAACTkAAAABAcb////c////AgAAAAk7AAAABSwAAAAwU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5OYW1lc3BhY2VJbmZvAgAAABNfZnVsbHlRdWFsaWZpZWROYW1lCV9wcm9qZWN0cwEENlN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5JUHJvamVjdEluZm9bXRwAAAACAAAABjwAAAAISW1hZ2VraXQJPQAAAAcvAAAAAAEAAAABAAAABDhTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVByb2plY3RGaWxlSW5mbxwAAAAJPgAAAAcxAAAAAAEAAAAAAAAABDdTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVJldHVyblR5cGVJbmZvHAAAAAU1AAAAMFN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLlJlZmxlY3Rpb24uUGFyYW1ldGVySW5mbwMAAAAFX25hbWUPX3JldHVyblR5cGVJbmZvB19wYXJlbnQBBAQxU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5SZXR1cm5UeXBlSW5mbwIAAAAtU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5NZXRob2RJbmZvAgAAAAIAAAAGPwAAAAN1cmwJQAAAAAkYAAAAATcAAAAhAAAABkIAAAAKUmVzdENsaWVudAG9////1f///wAAAAAKCUQAAAABAbv////T////AgAAAAkKAAAACUcAAAABOAAAABcAAAAKAAAAAAoJOQAAAAlJAAAACTIAAAABOQAAAAYAAAAJCgAAAAZMAAAADy5cUmVzdENsaWVudC5jcwc7AAAAAAEAAAABAAAABDZTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVBhcmFtZXRlckluZm8cAAAACU0AAAAHPQAAAAABAAAAAQAAAAQ0U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLklQcm9qZWN0SW5mbxwAAAAJTgAAAAE+AAAABgAAAAkKAAAABlAAAAAPLlxSZXN0Q2xpZW50LmNzAUAAAAAXAAAACgAAAAAKCSMAAAAJUgAAAAkfAAAAAUQAAAAsAAAACTwAAAAJVQAAAAdHAAAAAAEAAAABAAAABDhTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVByb2plY3RGaWxlSW5mbxwAAAAJVgAAAAdJAAAAAAEAAAAAAAAABDdTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVJldHVyblR5cGVJbmZvHAAAAAFNAAAANQAAAAk/AAAACVgAAAAJJwAAAAVOAAAALlN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLlJlZmxlY3Rpb24uUHJvamVjdEluZm8BAAAABV9uYW1lAQIAAAAJCgAAAAdSAAAAAAEAAAAAAAAABDdTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVJldHVyblR5cGVJbmZvHAAAAAdVAAAAAAEAAAABAAAABDRTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVByb2plY3RJbmZvHAAAAAlbAAAAAVYAAAAGAAAACQoAAAAGXQAAAA8uXFJlc3RDbGllbnQuY3MBWAAAABcAAAAKAAAAAAoJOQAAAAlfAAAACR8AAAABWwAAAE4AAAAJCgAAAAdfAAAAAAEAAAAAAAAABDdTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVJldHVyblR5cGVJbmZvHAAAAAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= - - - - - AAEAAAD/////AQAAAAAAAAAMAgAAAGBTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLCBWZXJzaW9uPTEuMS4yMi4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPTc5YmMwZGI5NzQwYTVmMDQFAQAAADhTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLkNvbGxlY3Rpb25zLlJ1bGVzQ29sbGVjdGlvbgMAAAANX2NvbHVtblNvcnRlcgZfY291bnQKSW5uZXJBcnJheQQABD9TdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLkNvbGxlY3Rpb25zLlJ1bGVzQ29sbGVjdGlvbitTb3J0ZXICAAAACCpTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJ1bGVzLklSdWxlW10CAAAAAgAAAAkDAAAABQAAAAkEAAAABQMAAAA/U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5Db2xsZWN0aW9ucy5SdWxlc0NvbGxlY3Rpb24rU29ydGVyAQAAAAxDb2x1bW5Ub1NvcnQBAgAAAAYFAAAAAAcEAAAAAAEAAABkAAAABChTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJ1bGVzLklSdWxlAgAAAAkGAAAACQcAAAAJCAAAAAkJAAAACQoAAAANXwwLAAAAaVN1Yk1haW4uQ29kZUl0UmlnaHQuUnVsZXMuUGVyZm9ybWFuY2UsIFZlcnNpb249MS4wLjAuMTQsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49MTBjYmVjNTdjZjEwZWFjYgwMAAAAXFN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLCBWZXJzaW9uPTEuMS4zNi4yMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj0xM2Y4NzgyYzBjODhiNTEwDA0AAABmU3ViTWFpbi5Db2RlSXRSaWdodC5Db2RlT2JqZWN0TW9kZWwsIFZlcnNpb249Mi4yLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iYTk2NGJjOWFkZjFlMTg5BQYAAAA4U3ViTWFpbi5Db2RlSXRSaWdodC5SdWxlcy5QZXJmb3JtYW5jZS5SZW1vdmVVbnVzZWRMb2NhbHMjAAAAFF91bnVzZWRMb2NhbFZhcmlhYmxlCF90YXJnZXRzCl9sYW5ndWFnZXMLX2NhbkNvcnJlY3QdX2RlZmF1bHRDb3JyZWN0aW9uT3B0aW9uSW5kZXgMX3Byb2plY3ROYW1lI1N1Yk1haW5QZXJmb3JtYW5jZVJ1bGVCYXNlK190YXJnZXRzJVN1Yk1haW5QZXJmb3JtYW5jZVJ1bGVCYXNlK19sYW5ndWFnZXMmU3ViTWFpblBlcmZvcm1hbmNlUnVsZUJhc2UrX2NhbkNvcnJlY3Q4U3ViTWFpblBlcmZvcm1hbmNlUnVsZUJhc2UrX2RlZmF1bHRDb3JyZWN0aW9uT3B0aW9uSW5kZXgnU3ViTWFpblBlcmZvcm1hbmNlUnVsZUJhc2UrX3Byb2plY3ROYW1lGFN1Yk1haW5SdWxlQmFzZStfdGFyZ2V0cxpTdWJNYWluUnVsZUJhc2UrX2xhbmd1YWdlcxtTdWJNYWluUnVsZUJhc2UrX2NhbkNvcnJlY3QtU3ViTWFpblJ1bGVCYXNlK19kZWZhdWx0Q29ycmVjdGlvbk9wdGlvbkluZGV4HFN1Yk1haW5SdWxlQmFzZStfcHJvamVjdE5hbWURUnVsZUJhc2UrX3RhcmdldHMTUnVsZUJhc2UrX2xhbmd1YWdlcxRSdWxlQmFzZStfY2FuQ29ycmVjdCZSdWxlQmFzZStfZGVmYXVsdENvcnJlY3Rpb25PcHRpb25JbmRleBVSdWxlQmFzZStfcHJvamVjdE5hbWUWQWJzdHJhY3RSdWxlQmFzZStfbmFtZRdBYnN0cmFjdFJ1bGVCYXNlK190aXRsZRpBYnN0cmFjdFJ1bGVCYXNlK19zZXZlcml0eRhBYnN0cmFjdFJ1bGVCYXNlK19zdGF0dXMYQWJzdHJhY3RSdWxlQmFzZStfc2NvcGVzGUFic3RyYWN0UnVsZUJhc2UrX2VsZW1lbnQZQWJzdHJhY3RSdWxlQmFzZStfdGFyZ2V0cxtBYnN0cmFjdFJ1bGVCYXNlK19sYW5ndWFnZXMcQWJzdHJhY3RSdWxlQmFzZStfY2FuQ29ycmVjdBZBYnN0cmFjdFJ1bGVCYXNlK19ub3RlLkFic3RyYWN0UnVsZUJhc2UrX2RlZmF1bHRDb3JyZWN0aW9uT3B0aW9uSW5kZXgdQWJzdHJhY3RSdWxlQmFzZStfcHJvamVjdE5hbWUeQWJzdHJhY3RSdWxlQmFzZStfcmVsYXRpdmVQYXRoIEFic3RyYWN0UnVsZUJhc2UrX2lzU3R5bGVDb3BSdWxlBAQEAAABBAQAAAEEBAAAAQQEAAABAQEEBAQEBAQABAABAQAvU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5WYXJpYWJsZUluZm8MAAAALlN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUnVsZXMuUnVsZVRhcmdldHMCAAAALFN1Yk1haW4uQ29kZU9iamVjdE1vZGVsLlJlZmxlY3Rpb24uTGFuZ3VhZ2VzDQAAAAEILlN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUnVsZXMuUnVsZVRhcmdldHMCAAAALFN1Yk1haW4uQ29kZU9iamVjdE1vZGVsLlJlZmxlY3Rpb24uTGFuZ3VhZ2VzDQAAAAEILlN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUnVsZXMuUnVsZVRhcmdldHMCAAAALFN1Yk1haW4uQ29kZU9iamVjdE1vZGVsLlJlZmxlY3Rpb24uTGFuZ3VhZ2VzDQAAAAEILlN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUnVsZXMuUnVsZVRhcmdldHMCAAAALFN1Yk1haW4uQ29kZU9iamVjdE1vZGVsLlJlZmxlY3Rpb24uTGFuZ3VhZ2VzDQAAAAEIMFN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUnVsZXMuU2V2ZXJpdHlMZXZlbAIAAAAvU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SdWxlcy5BY3RpdmVTdGF0dXMCAAAALVN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUnVsZXMuUnVsZVNjb3BlcwIAAAAtU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5NZXRob2RJbmZvDAAAAC5TdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJ1bGVzLlJ1bGVUYXJnZXRzAgAAACxTdWJNYWluLkNvZGVPYmplY3RNb2RlbC5SZWZsZWN0aW9uLkxhbmd1YWdlcw0AAAABJ1N1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUnVsZXMuTm90ZQIAAAAIDwsAAAAJDgAAAAXx////LlN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUnVsZXMuUnVsZVRhcmdldHMBAAAAB3ZhbHVlX18ACQIAAAAAYAAAAAAAAAXw////LFN1Yk1haW4uQ29kZU9iamVjdE1vZGVsLlJlZmxlY3Rpb24uTGFuZ3VhZ2VzAQAAAAd2YWx1ZV9fAAgNAAAAAwAAAAAAAAAABhEAAAALQ29uc29sZUFwcDEB7v////H///8AYAAAAAAAAAHt////8P///wMAAAAAAAAAAAkRAAAAAez////x////AGAAAAAAAAAB6/////D///8DAAAAAAAAAAAJEQAAAAHq////8f///wBgAAAAAAAAAen////w////AwAAAAAAAAAACREAAAAGGAAAAB5NZXRob2QgLT4gUmVtb3ZlIHVudXNlZCBsb2NhbHMGGQAAABRSZW1vdmUgdW51c2VkIGxvY2FscwXm////MFN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUnVsZXMuU2V2ZXJpdHlMZXZlbAEAAAAHdmFsdWVfXwAIAgAAAAIAAAAF5f///y9TdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJ1bGVzLkFjdGl2ZVN0YXR1cwEAAAAHdmFsdWVfXwAIAgAAAAEAAAAF5P///y1TdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJ1bGVzLlJ1bGVTY29wZXMBAAAAB3ZhbHVlX18ACAIAAAD/nxUACR0AAAAB4v////H///8AYAAAAAAAAAHh////8P///wMAAAAACgAAAAAJEQAAAAYhAAAADC5cUHJvZ3JhbS5jcwIAAAAMIgAAAGRTdWJNYWluLkNvZGVJdFJpZ2h0LlJ1bGVzLk5hbWluZywgVmVyc2lvbj0xLjAuMC4xMSwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj0wYmVhNGY3YjdiOGVhZGI2DCMAAABJU3lzdGVtLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OQUHAAAAQlN1Yk1haW4uQ29kZUl0UmlnaHQuUnVsZXMuTmFtaW5nLklkZW50aWZpZXJzU2hvdWxkQmVDYXNlZENvcnJlY3RseSkAAAAPX2lkZW50aWZpZXJDYXNlFV9wcm9jZXNzRXZlbnRIYW5kbGVycw5fZXhjbHVkZWRXb3JkcxVfY2FuQ29ycmVjdFdhc0NoYW5nZWQTX3VzZXJDb3JyZWN0T3B0aW9ucwhfdGFyZ2V0cwpfbGFuZ3VhZ2VzC19jYW5Db3JyZWN0HV9kZWZhdWx0Q29ycmVjdGlvbk9wdGlvbkluZGV4DF9wcm9qZWN0TmFtZSRTdWJNYWluTmFtaW5nUnVsZUJhc2UrX2V4Y2x1ZGVkV29yZHMrU3ViTWFpbk5hbWluZ1J1bGVCYXNlK19jYW5Db3JyZWN0V2FzQ2hhbmdlZClTdWJNYWluTmFtaW5nUnVsZUJhc2UrX3VzZXJDb3JyZWN0T3B0aW9ucx5TdWJNYWluTmFtaW5nUnVsZUJhc2UrX3RhcmdldHMgU3ViTWFpbk5hbWluZ1J1bGVCYXNlK19sYW5ndWFnZXMhU3ViTWFpbk5hbWluZ1J1bGVCYXNlK19jYW5Db3JyZWN0M1N1Yk1haW5OYW1pbmdSdWxlQmFzZStfZGVmYXVsdENvcnJlY3Rpb25PcHRpb25JbmRleCJTdWJNYWluTmFtaW5nUnVsZUJhc2UrX3Byb2plY3ROYW1lHU5hbWluZ1J1bGVCYXNlK19leGNsdWRlZFdvcmRzJE5hbWluZ1J1bGVCYXNlK19jYW5Db3JyZWN0V2FzQ2hhbmdlZBtOYW1pbmdSdWxlQmFzZStfZXhjbHVkZUxpc3QiTmFtaW5nUnVsZUJhc2UrX3VzZXJDb3JyZWN0T3B0aW9ucxdOYW1pbmdSdWxlQmFzZStfdGFyZ2V0cxlOYW1pbmdSdWxlQmFzZStfbGFuZ3VhZ2VzGk5hbWluZ1J1bGVCYXNlK19jYW5Db3JyZWN0LE5hbWluZ1J1bGVCYXNlK19kZWZhdWx0Q29ycmVjdGlvbk9wdGlvbkluZGV4G05hbWluZ1J1bGVCYXNlK19wcm9qZWN0TmFtZRZBYnN0cmFjdFJ1bGVCYXNlK19uYW1lF0Fic3RyYWN0UnVsZUJhc2UrX3RpdGxlGkFic3RyYWN0UnVsZUJhc2UrX3NldmVyaXR5GEFic3RyYWN0UnVsZUJhc2UrX3N0YXR1cxhBYnN0cmFjdFJ1bGVCYXNlK19zY29wZXMZQWJzdHJhY3RSdWxlQmFzZStfZWxlbWVudBlBYnN0cmFjdFJ1bGVCYXNlK190YXJnZXRzG0Fic3RyYWN0UnVsZUJhc2UrX2xhbmd1YWdlcxxBYnN0cmFjdFJ1bGVCYXNlK19jYW5Db3JyZWN0FkFic3RyYWN0UnVsZUJhc2UrX25vdGUuQWJzdHJhY3RSdWxlQmFzZStfZGVmYXVsdENvcnJlY3Rpb25PcHRpb25JbmRleB1BYnN0cmFjdFJ1bGVCYXNlK19wcm9qZWN0TmFtZR5BYnN0cmFjdFJ1bGVCYXNlK19yZWxhdGl2ZVBhdGggQWJzdHJhY3RSdWxlQmFzZStfaXNTdHlsZUNvcFJ1bGUEAAYABAQEAAABBgAEBAQAAAEGAAEEBAQAAAEBAQQEBAQEBAAEAAEBACtTdWJNYWluLkNvZGVJdFJpZ2h0LlJ1bGVzLk5hbWluZy5DYXNlT3B0aW9uIgAAAAEBL1N5c3RlbS5Db2xsZWN0aW9ucy5TcGVjaWFsaXplZC5TdHJpbmdDb2xsZWN0aW9uIwAAAC5TdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJ1bGVzLlJ1bGVUYXJnZXRzAgAAACxTdWJNYWluLkNvZGVPYmplY3RNb2RlbC5SZWZsZWN0aW9uLkxhbmd1YWdlcw0AAAABCAEvU3lzdGVtLkNvbGxlY3Rpb25zLlNwZWNpYWxpemVkLlN0cmluZ0NvbGxlY3Rpb24jAAAALlN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUnVsZXMuUnVsZVRhcmdldHMCAAAALFN1Yk1haW4uQ29kZU9iamVjdE1vZGVsLlJlZmxlY3Rpb24uTGFuZ3VhZ2VzDQAAAAEIAS9TeXN0ZW0uQ29sbGVjdGlvbnMuU3BlY2lhbGl6ZWQuU3RyaW5nQ29sbGVjdGlvbiMAAAAuU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SdWxlcy5SdWxlVGFyZ2V0cwIAAAAsU3ViTWFpbi5Db2RlT2JqZWN0TW9kZWwuUmVmbGVjdGlvbi5MYW5ndWFnZXMNAAAAAQgwU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SdWxlcy5TZXZlcml0eUxldmVsAgAAAC9TdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJ1bGVzLkFjdGl2ZVN0YXR1cwIAAAAtU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SdWxlcy5SdWxlU2NvcGVzAgAAAC1TdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5SZWZsZWN0aW9uLk1ldGhvZEluZm8MAAAALlN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUnVsZXMuUnVsZVRhcmdldHMCAAAALFN1Yk1haW4uQ29kZU9iamVjdE1vZGVsLlJlZmxlY3Rpb24uTGFuZ3VhZ2VzDQAAAAEnU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SdWxlcy5Ob3RlAgAAAAgPIgAAAAXc////K1N1Yk1haW4uQ29kZUl0UmlnaHQuUnVsZXMuTmFtaW5nLkNhc2VPcHRpb24BAAAAB3ZhbHVlX18ACCIAAAACAAAAAAoACSUAAAAB2v////H///8A4BcAAAAAAAHZ////8P///wMAAAABAAAAAAkRAAAACgAJJQAAAAHW////8f///wDgFwAAAAAAAdX////w////AwAAAAEAAAAACREAAAAKAAoJJQAAAAHS////8f///wDgFwAAAAAAAdH////w////AwAAAAEAAAAACREAAAAGMQAAABZNZW1iZXIgLT4gUGFzY2FsIENhc2VkBjIAAAAsTWVtYmVyIGlkZW50aWZpZXJzIHNob3VsZCBiZSBjYXNlZCBjb3JyZWN0bHkBzf///+b///8CAAAAAcz////l////AQAAAAHL////5P////63NQAJNgAAAAHJ////8f///wDgFwAAAAAAAcj////w////AwAAAAEKAAAAAAkRAAAABjoAAAAMLlxQcm9ncmFtLmNzAgAAAAEIAAAABgAAAAk7AAAAAcT////x////AGAAAAAAAAABw/////D///8DAAAAAAAAAAAJEQAAAAHB////8f///wBgAAAAAAAAAcD////w////AwAAAAAAAAAACREAAAABv/////H///8AYAAAAAAAAAG+////8P///wMAAAAAAAAAAAkRAAAAAb3////x////AGAAAAAAAAABvP////D///8DAAAAAAAAAAAJEQAAAAZFAAAAHk1ldGhvZCAtPiBSZW1vdmUgdW51c2VkIGxvY2FscwZGAAAAFFJlbW92ZSB1bnVzZWQgbG9jYWxzAbn////m////AgAAAAG4////5f///wEAAAABt////+T/////nxUACTYAAAABtf////H///8AYAAAAAAAAAG0////8P///wMAAAAACgAAAAAJEQAAAAZOAAAADC5cUHJvZ3JhbS5jcwIAAAABCQAAAAcAAAABsf///9z///8CAAAAAAoACVAAAAABr/////H///8A4BcAAAAAAAGu////8P///wMAAAABAAAAAAkRAAAACgAJUAAAAAGr////8f///wDgFwAAAAAAAar////w////AwAAAAEAAAAACREAAAAKAAoJUAAAAAGn////8f///wDgFwAAAAAAAab////w////AwAAAAEAAAAACREAAAAGXAAAABZNZW1iZXIgLT4gUGFzY2FsIENhc2VkBl0AAAAsTWVtYmVyIGlkZW50aWZpZXJzIHNob3VsZCBiZSBjYXNlZCBjb3JyZWN0bHkBov///+b///8CAAAAAaH////l////AQAAAAGg////5P////63NQAJYQAAAAGe////8f///wDgFwAAAAAAAZ3////w////AwAAAAEKAAAAAAkRAAAABmUAAAAMLlxQcm9ncmFtLmNzAgAAAAEKAAAABgAAAAlmAAAAAZn////x////AGAAAAAAAAABmP////D///8DAAAAAAAAAAAJEQAAAAGW////8f///wBgAAAAAAAAAZX////w////AwAAAAAAAAAACREAAAABlP////H///8AYAAAAAAAAAGT////8P///wMAAAAAAAAAAAkRAAAAAZL////x////AGAAAAAAAAABkf////D///8DAAAAAAAAAAAJEQAAAAZwAAAAHk1ldGhvZCAtPiBSZW1vdmUgdW51c2VkIGxvY2FscwZxAAAAFFJlbW92ZSB1bnVzZWQgbG9jYWxzAY7////m////AgAAAAGN////5f///wEAAAABjP///+T/////nxUACWEAAAABiv////H///8AYAAAAAAAAAGJ////8P///wMAAAAACgAAAAAJEQAAAAZ5AAAADC5cUHJvZ3JhbS5jcwIAAAAFDgAAAC9TdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5SZWZsZWN0aW9uLlZhcmlhYmxlSW5mbwQAAAAFX25hbWUPX3JldHVyblR5cGVJbmZvB19wYXJlbnQKX3N0YXRlbWVudAEEBAQxU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5SZXR1cm5UeXBlSW5mbwwAAAAtU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5NZXRob2RJbmZvDAAAADBTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5SZWZsZWN0aW9uLlN0YXRlbWVudEluZm8MAAAADAAAAAZ6AAAABXRva2VuCXsAAAAJfAAAAAl9AAAABR0AAAAtU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5NZXRob2RJbmZvBwAAAAVfbmFtZQdfcGFyZW50C19yZXR1cm5UeXBlDF9wcm9qZWN0RmlsZQ5fY2FzZVNlbnNpdGl2ZQpfbW9kaWZpZXJzC19wYXJhbWV0ZXJzAQQEBAAEBCxTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5SZWZsZWN0aW9uLkNsYXNzSW5mbwwAAAAxU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5SZXR1cm5UeXBlSW5mbwwAAAAyU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5Qcm9qZWN0RmlsZUluZm8MAAAAATtTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uTWVtYmVySW5mb01vZGlmaWVycwIAAAA4U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLklQYXJhbWV0ZXJJbmZvW10CAAAADAAAAAZ+AAAABE1haW4JfwAAAAmAAAAACYEAAAABBX7///87U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLk1lbWJlckluZm9Nb2RpZmllcnMBAAAAB3ZhbHVlX18ACAIAAACQAAAACYMAAAAFJQAAAC9TeXN0ZW0uQ29sbGVjdGlvbnMuU3BlY2lhbGl6ZWQuU3RyaW5nQ29sbGVjdGlvbgEAAAAEZGF0YQMcU3lzdGVtLkNvbGxlY3Rpb25zLkFycmF5TGlzdCMAAAAJhAAAAAE2AAAAHQAAAAaFAAAACWdldEJhc2U2NAmGAAAACYcAAAAJiAAAAAEBd////37///+QAAAACgE7AAAADgAAAAaKAAAAGWJhc2U2NEltYWdlUmVwcmVzZW50YXRpb24JiwAAAAmMAAAACY0AAAABUAAAACUAAAAJjgAAAAFhAAAAHQAAAAaPAAAADGdldEJhc2U2NFVSSQmQAAAACZEAAAAJkgAAAAEBbf///37///+QAAAACgFmAAAADgAAAAaUAAAACWNvbnZlcnRlZAmVAAAACZYAAAAJlwAAAAV7AAAAMVN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLlJlZmxlY3Rpb24uUmV0dXJuVHlwZUluZm8GAAAABV90eXBlFF9wb2ludGVyTmVzdGluZ0xldmVsEF9hcnJheURpbWVuc2lvbnMMX3Byb2plY3RGaWxlEV9nZW5lcmljQXJndW1lbnRzE19mdWxseVF1YWxpZmllZE5hbWUEAAcEBAExU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLklUeXBlSW5mbwIAAAAICDJTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5SZWZsZWN0aW9uLlByb2plY3RGaWxlSW5mbwwAAAA5U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLklSZXR1cm5UeXBlSW5mb1tdAgAAAAwAAAAKAAAAAAoJmAAAAAmZAAAABpoAAAADdmFyAXwAAAAdAAAACX4AAAAJnAAAAAmdAAAACZ4AAAABAWH///9+////kAAAAAmgAAAABX0AAAAwU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5TdGF0ZW1lbnRJbmZvBAAAABhCbG9ja0luZm8rX3N0YXRlbWVudFR5cGUVQmxvY2tJbmZvK19tZW1iZXJJbmZvEUJsb2NrSW5mbytfcGFyZW50EEJsb2NrSW5mbytfaW5kZXgEBAQAMFN1Yk1haW4uQ29kZU9iamVjdE1vZGVsLlJlZmxlY3Rpb24uU3RhdGVtZW50VHlwZQ0AAAAtU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5NZXRob2RJbmZvDAAAADZTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVN0YXRlbWVudEluZm8CAAAACAwAAAAFX////zBTdWJNYWluLkNvZGVPYmplY3RNb2RlbC5SZWZsZWN0aW9uLlN0YXRlbWVudFR5cGUBAAAAB3ZhbHVlX18ACA0AAAADAAAACaIAAAAKAAAAAAV/AAAALFN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLlJlZmxlY3Rpb24uQ2xhc3NJbmZvCAAAAAVfbmFtZQpfY2xhc3NUeXBlB19wYXJlbnQOX25hbWVzcGFjZUluZm8OX2Nhc2VTZW5zaXRpdmUKX21vZGlmaWVycwxfcHJvamVjdE5hbWUNX3Byb2plY3RGaWxlcwEEBAQABAEENVN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5DbGFzc0luZm9UeXBlAgAAADFTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVR5cGVJbmZvAgAAADBTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5SZWZsZWN0aW9uLk5hbWVzcGFjZUluZm8MAAAAATJTdWJNYWluLkNvZGVPYmplY3RNb2RlbC5SZWZsZWN0aW9uLk1lbWJlck1vZGlmaWVycw0AAAA6U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLklQcm9qZWN0RmlsZUluZm9bXQIAAAAMAAAABqMAAAAHUHJvZ3JhbQVc////NVN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5DbGFzc0luZm9UeXBlAQAAAAd2YWx1ZV9fAAgCAAAAAAAAAAoJpQAAAAEFWv///zJTdWJNYWluLkNvZGVPYmplY3RNb2RlbC5SZWZsZWN0aW9uLk1lbWJlck1vZGlmaWVycwEAAAAHdmFsdWVfXwAIDQAAAAgAAAAJEQAAAAmoAAAAAYAAAAB7AAAACgAAAAAKCYEAAAAJqgAAAAarAAAAC1N5c3RlbS5Wb2lkBYEAAAAyU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5Qcm9qZWN0RmlsZUluZm8CAAAADF9wcm9qZWN0TmFtZRFfcmVsYXRpdmVGaWxlTmFtZQEBDAAAAAkRAAAABq0AAAAMLlxQcm9ncmFtLmNzB4MAAAAAAQAAAAEAAAAENlN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5JUGFyYW1ldGVySW5mbwIAAAAJrgAAAASEAAAAHFN5c3RlbS5Db2xsZWN0aW9ucy5BcnJheUxpc3QDAAAABl9pdGVtcwVfc2l6ZQhfdmVyc2lvbgUAAAgICa8AAAABAAAAAQAAAAGGAAAAfwAAAAawAAAAB1Byb2dyYW0BT////1z///8AAAAACgmyAAAAAQFN////Wv///wgAAAAJEQAAAAm1AAAAAYcAAAB7AAAACgAAAAAJtgAAAAmIAAAACbgAAAAGuQAAAAtTeXN0ZW0uQnl0ZQGIAAAAgQAAAAkRAAAABrsAAAAMLlxQcm9ncmFtLmNzAYsAAAB7AAAACgAAAAAKCbwAAAAJvQAAAAa+AAAADVN5c3RlbS5TdHJpbmcBjAAAAB0AAAAJhQAAAAnAAAAACcEAAAAJwgAAAAEBPf///37///+QAAAACgGNAAAAfQAAAAE8////X////wMAAAAJxQAAAAoCAAAAAY4AAACEAAAACcYAAAABAAAAAQAAAAGQAAAAfwAAAAbHAAAAB1Byb2dyYW0BOP///1z///8AAAAACgnJAAAAAQE2////Wv///wgAAAAJEQAAAAnMAAAAAZEAAAB7AAAACgAAAAAKCZIAAAAJzgAAAAbPAAAAA1VyaQGSAAAAgQAAAAkRAAAABtEAAAAMLlxQcm9ncmFtLmNzAZUAAAB7AAAACgAAAAAKCdIAAAAJ0wAAAAbUAAAAA3ZhcgGWAAAAHQAAAAmPAAAACdYAAAAJ1wAAAAnYAAAAAQEn////fv///5AAAAAKAZcAAAB9AAAAASb///9f////AwAAAAnbAAAACgIAAAABmAAAAIEAAAAJEQAAAAbdAAAADC5cUHJvZ3JhbS5jcweZAAAAAAEAAAAAAAAABDdTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVJldHVyblR5cGVJbmZvAgAAAAGcAAAAfwAAAAbeAAAAB1Byb2dyYW0BIf///1z///8AAAAACgngAAAAAQEf////Wv///wgAAAAJEQAAAAnjAAAAAZ0AAAB7AAAACgAAAAAKCZ4AAAAJ5QAAAAmrAAAAAZ4AAACBAAAACREAAAAG6AAAAAwuXFByb2dyYW0uY3MHoAAAAAABAAAAAQAAAAQ2U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLklQYXJhbWV0ZXJJbmZvAgAAAAnpAAAAAaIAAAAdAAAACX4AAAAJ6wAAAAnsAAAACe0AAAABARL///9+////kAAAAAnvAAAABaUAAAAwU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5OYW1lc3BhY2VJbmZvAgAAABNfZnVsbHlRdWFsaWZpZWROYW1lCV9wcm9qZWN0cwEENlN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5JUHJvamVjdEluZm9bXQIAAAAMAAAABvAAAAALQ29uc29sZUFwcDEJ8QAAAAeoAAAAAAEAAAABAAAABDhTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVByb2plY3RGaWxlSW5mbwIAAAAJ8gAAAAeqAAAAAAEAAAAAAAAABDdTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVJldHVyblR5cGVJbmZvAgAAAAWuAAAAMFN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLlJlZmxlY3Rpb24uUGFyYW1ldGVySW5mbwMAAAAFX25hbWUPX3JldHVyblR5cGVJbmZvB19wYXJlbnQBBAQxU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5SZXR1cm5UeXBlSW5mbwwAAAAtU3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuUmVmbGVjdGlvbi5NZXRob2RJbmZvDAAAAAwAAAAG8wAAAARhcmdzCfQAAAAJHQAAABCvAAAABAAAAAb2AAAACUdldEJhc2U2NA0DAbIAAAClAAAACfAAAAAJ+AAAAAe1AAAAAAEAAAABAAAABDhTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVByb2plY3RGaWxlSW5mbwIAAAAJ+QAAAA+2AAAAAQAAAAgBAAAAB7gAAAAAAQAAAAAAAAAEN1N1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5JUmV0dXJuVHlwZUluZm8CAAAAAbwAAACBAAAACREAAAAG+wAAAAwuXFByb2dyYW0uY3MHvQAAAAABAAAAAAAAAAQ3U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLklSZXR1cm5UeXBlSW5mbwIAAAABwAAAAH8AAAAG/AAAAAdQcm9ncmFtAQP///9c////AAAAAAoJ/gAAAAEBAf///1r///8IAAAACREAAAAJAQEAAAHBAAAAewAAAAoAAAAACbYAAAAJwgAAAAkEAQAACbkAAAABwgAAAIEAAAAJEQAAAAYHAQAADC5cUHJvZ3JhbS5jcwHFAAAAHQAAAAmFAAAACQkBAAAJCgEAAAkLAQAAAQH0/v//fv///5AAAAAKEMYAAAAEAAAABg0BAAAMR2V0QmFzZTY0VXJpDQMByQAAAKUAAAAJ8AAAAAkPAQAAB8wAAAAAAQAAAAEAAAAEOFN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5JUHJvamVjdEZpbGVJbmZvAgAAAAkQAQAAB84AAAAAAQAAAAAAAAAEN1N1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5JUmV0dXJuVHlwZUluZm8CAAAAAdIAAACBAAAACREAAAAGEgEAAAwuXFByb2dyYW0uY3MH0wAAAAABAAAAAAAAAAQ3U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLklSZXR1cm5UeXBlSW5mbwIAAAAB1gAAAH8AAAAGEwEAAAdQcm9ncmFtAez+//9c////AAAAAAoJFQEAAAEB6v7//1r///8IAAAACREAAAAJGAEAAAHXAAAAewAAAAoAAAAACgnYAAAACRoBAAAJzwAAAAHYAAAAgQAAAAkRAAAABh0BAAAMLlxQcm9ncmFtLmNzAdsAAAAdAAAACY8AAAAJHwEAAAkgAQAACSEBAAABAd7+//9+////kAAAAAoB4AAAAKUAAAAJ8AAAAAkkAQAAB+MAAAAAAQAAAAEAAAAEOFN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5JUHJvamVjdEZpbGVJbmZvAgAAAAklAQAAB+UAAAAAAQAAAAAAAAAEN1N1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5JUmV0dXJuVHlwZUluZm8CAAAAAekAAACuAAAACfMAAAAJJwEAAAl8AAAAAesAAAB/AAAABikBAAAHUHJvZ3JhbQHW/v//XP///wAAAAAKCSsBAAABAdT+//9a////CAAAAAkRAAAACS4BAAAB7AAAAHsAAAAKAAAAAAoJ7QAAAAkwAQAACasAAAAB7QAAAIEAAAAJEQAAAAYzAQAADC5cUHJvZ3JhbS5jcwfvAAAAAAEAAAABAAAABDZTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVBhcmFtZXRlckluZm8CAAAACTQBAAAH8QAAAAABAAAAAQAAAAQ0U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLklQcm9qZWN0SW5mbwIAAAAJNQEAAAHyAAAAgQAAAAkRAAAABjcBAAAMLlxQcm9ncmFtLmNzAfQAAAB7AAAACgAAAAAJOAEAAAmBAAAACToBAAAJvgAAAAf4AAAAAAEAAAABAAAABDRTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVByb2plY3RJbmZvAgAAAAk8AQAAAfkAAACBAAAACREAAAAGPgEAAAwuXFByb2dyYW0uY3MB/gAAAKUAAAAJ8AAAAAlAAQAABwEBAAAAAQAAAAEAAAAEOFN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5JUHJvamVjdEZpbGVJbmZvAgAAAAlBAQAABwQBAAAAAQAAAAAAAAAEN1N1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5JUmV0dXJuVHlwZUluZm8CAAAAAQkBAAB/AAAABkIBAAAHUHJvZ3JhbQG9/v//XP///wAAAAAKCUQBAAABAbv+//9a////CAAAAAkRAAAACUcBAAABCgEAAHsAAAAKAAAAAAm2AAAACQsBAAAJSgEAAAm5AAAAAQsBAACBAAAACREAAAAGTQEAAAwuXFByb2dyYW0uY3MHDwEAAAABAAAAAQAAAAQ0U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLklQcm9qZWN0SW5mbwIAAAAJTgEAAAEQAQAAgQAAAAkRAAAABlABAAAMLlxQcm9ncmFtLmNzARUBAAClAAAACfAAAAAJUgEAAAcYAQAAAAEAAAABAAAABDhTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVByb2plY3RGaWxlSW5mbwIAAAAJUwEAAAcaAQAAAAEAAAAAAAAABDdTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVJldHVyblR5cGVJbmZvAgAAAAEfAQAAfwAAAAZUAQAAB1Byb2dyYW0Bq/7//1z///8AAAAACglWAQAAAQGp/v//Wv///wgAAAAJEQAAAAlZAQAAASABAAB7AAAACgAAAAAKCSEBAAAJWwEAAAnPAAAAASEBAACBAAAACREAAAAGXgEAAAwuXFByb2dyYW0uY3MHJAEAAAABAAAAAQAAAAQ0U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLklQcm9qZWN0SW5mbwIAAAAJXwEAAAElAQAAgQAAAAkRAAAABmEBAAAMLlxQcm9ncmFtLmNzAScBAAB7AAAACgAAAAAJOAEAAAmeAAAACWQBAAAJvgAAAAErAQAApQAAAAnwAAAACWcBAAAHLgEAAAABAAAAAQAAAAQ4U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLklQcm9qZWN0RmlsZUluZm8CAAAACWgBAAAHMAEAAAABAAAAAAAAAAQ3U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLklSZXR1cm5UeXBlSW5mbwIAAAABNAEAAK4AAAAJ8wAAAAlqAQAACaIAAAAFNQEAAC5TdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5SZWZsZWN0aW9uLlByb2plY3RJbmZvAQAAAAVfbmFtZQEMAAAACREAAAAPOAEAAAEAAAAIAQAAAAc6AQAAAAEAAAAAAAAABDdTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVJldHVyblR5cGVJbmZvAgAAAAE8AQAANQEAAAkRAAAAB0ABAAAAAQAAAAEAAAAENFN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5JUHJvamVjdEluZm8CAAAACW0BAAABQQEAAIEAAAAJEQAAAAZvAQAADC5cUHJvZ3JhbS5jcwFEAQAApQAAAAnwAAAACXEBAAAHRwEAAAABAAAAAQAAAAQ4U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLklQcm9qZWN0RmlsZUluZm8CAAAACXIBAAAHSgEAAAABAAAAAAAAAAQ3U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLklSZXR1cm5UeXBlSW5mbwIAAAABTgEAADUBAAAJEQAAAAdSAQAAAAEAAAABAAAABDRTdWJNYWluLkNvZGVJdFJpZ2h0LlNkay5Db3JlLlJlZmxlY3Rpb24uSVByb2plY3RJbmZvAgAAAAl0AQAAAVMBAACBAAAACREAAAAGdgEAAAwuXFByb2dyYW0uY3MBVgEAAKUAAAAJ8AAAAAl4AQAAB1kBAAAAAQAAAAEAAAAEOFN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5JUHJvamVjdEZpbGVJbmZvAgAAAAl5AQAAB1sBAAAAAQAAAAAAAAAEN1N1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5JUmV0dXJuVHlwZUluZm8CAAAAAV8BAAA1AQAACREAAAAHZAEAAAABAAAAAAAAAAQ3U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLklSZXR1cm5UeXBlSW5mbwIAAAAHZwEAAAABAAAAAQAAAAQ0U3ViTWFpbi5Db2RlSXRSaWdodC5TZGsuQ29yZS5SZWZsZWN0aW9uLklQcm9qZWN0SW5mbwIAAAAJewEAAAFoAQAAgQAAAAkRAAAABn0BAAAMLlxQcm9ncmFtLmNzAWoBAAB7AAAACgAAAAAJOAEAAAntAAAACYABAAAJvgAAAAFtAQAANQEAAAkRAAAAB3EBAAAAAQAAAAEAAAAENFN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5JUHJvamVjdEluZm8CAAAACYMBAAABcgEAAIEAAAAJEQAAAAaFAQAADC5cUHJvZ3JhbS5jcwF0AQAANQEAAAkRAAAAB3gBAAAAAQAAAAEAAAAENFN1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5JUHJvamVjdEluZm8CAAAACYcBAAABeQEAAIEAAAAJEQAAAAaJAQAADC5cUHJvZ3JhbS5jcwF7AQAANQEAAAkRAAAAB4ABAAAAAQAAAAAAAAAEN1N1Yk1haW4uQ29kZUl0UmlnaHQuU2RrLkNvcmUuUmVmbGVjdGlvbi5JUmV0dXJuVHlwZUluZm8CAAAAAYMBAAA1AQAACREAAAABhwEAADUBAAAJEQAAAAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== - \ No newline at end of file diff --git a/Imagekit.sln b/Imagekit.sln index 84305571..a0ecefd6 100644 --- a/Imagekit.sln +++ b/Imagekit.sln @@ -1,43 +1,38 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.2.32630.192 +VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Imagekit", "Imagekit\Imagekit.csproj", "{D7929DCF-13EC-4677-9859-D55DA9D752E4}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Imagekit", "src\Imagekit\Imagekit.csproj", "{5816A0C1-3BA1-454E-8D08-85B23DEF309D}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C4C9DA70-E3C7-4848-AB58-E93762408560}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Imagekit.Tests", "src\Imagekit.Tests\Imagekit.Tests.csproj", "{0732C8A6-7313-4C33-AE2E-FFAA82EFB481}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93E58BA5-CEFE-447E-AC0C-F2C5BC4C411D}" ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig + CHANGELOG.md + .csharpierignore + .editorconfig + .gitignore + LICENSE + README.md + SECURITY.md EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Imagekit.UnitTests", "Imagekit.UnitTests\Imagekit.UnitTests.csproj", "{B459E977-A7C2-47CB-A743-2F0AAFCE8178}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D7929DCF-13EC-4677-9859-D55DA9D752E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D7929DCF-13EC-4677-9859-D55DA9D752E4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D7929DCF-13EC-4677-9859-D55DA9D752E4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D7929DCF-13EC-4677-9859-D55DA9D752E4}.Release|Any CPU.Build.0 = Release|Any CPU - {B459E977-A7C2-47CB-A743-2F0AAFCE8178}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B459E977-A7C2-47CB-A743-2F0AAFCE8178}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B459E977-A7C2-47CB-A743-2F0AAFCE8178}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B459E977-A7C2-47CB-A743-2F0AAFCE8178}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {CCE1EF90-4EA0-4458-90C1-7F408AE6E292} - EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - Policies = $0 - $0.DotNetNamingPolicy = $1 - $1.DirectoryNamespaceAssociation = PrefixedHierarchical - $0.StandardHeader = $2 - $0.VersionControlPolicy = $3 + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5816A0C1-3BA1-454E-8D08-85B23DEF309D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5816A0C1-3BA1-454E-8D08-85B23DEF309D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5816A0C1-3BA1-454E-8D08-85B23DEF309D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5816A0C1-3BA1-454E-8D08-85B23DEF309D}.Release|Any CPU.Build.0 = Release|Any CPU + {0732C8A6-7313-4C33-AE2E-FFAA82EFB481}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0732C8A6-7313-4C33-AE2E-FFAA82EFB481}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0732C8A6-7313-4C33-AE2E-FFAA82EFB481}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0732C8A6-7313-4C33-AE2E-FFAA82EFB481}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Imagekit.sln.CodeItRight.xml b/Imagekit.sln.CodeItRight.xml deleted file mode 100644 index ee2ebdb7..00000000 --- a/Imagekit.sln.CodeItRight.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - *.min.js - jquery*.js - - diff --git a/Imagekit/Constant/UrlHandler.cs b/Imagekit/Constant/UrlHandler.cs deleted file mode 100644 index 07ef3c3b..00000000 --- a/Imagekit/Constant/UrlHandler.cs +++ /dev/null @@ -1,42 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit.Constant -{ - public static class UrlHandler - { - public const string MediaAPIBaseUrl = "https://api.imagekit.io/"; - public const string UploadAPIBaseUrl = "https://upload.imagekit.io/"; - public const string UploadFile = @"api/v1/files/upload"; - public const string UpdateFileRequest = @"v1/files/{0}/details"; - public const string GetFileRequest = @"v1/files/?{0}"; - public const string GetPurge = @"v1/files/purge"; - public const string GetPurgeStatus = @"v1/files/purge/{0}"; - public const string GetFileDetails = @"v1/files/{0}/details"; - public const string DeleteFile = @"v1/files/{0}/"; - public const string BulkDelete = @"v1/files/batch/deleteByfileIds"; - public const string GetMetaData = @"v1/files/{0}/metadata"; - public const string GetRemoteData = @"v1/metadata?url={0}"; - public const string RemoveTags = @"v1/files/removeTags"; - public const string AddTags = @"v1/files/addTags"; - public const string RemoveAITags = @"v1/files/removeAITags"; - public const string CustomMetadataFields = @"v1/customMetadataFields?includeDeleted={0}"; - public const string CreareCustomMetaDataFields = @"v1/customMetadataFields"; - public const string DeleteCustomMetaDataFields = @"v1/customMetadataFields/{0}"; - public const string UpdateCustomMetadataFields = @"v1/customMetadataFields/{0}"; - public const string DeleteVesrion = @"v1/files/{0}/versions/{1}"; - public const string CopyFile = @"v1/files/copy"; - public const string MoveFile = @"v1/files/move"; - public const string RenameFile = @"v1/files/rename"; - public const string RestoreVesrion = @"v1/files/{0}/versions/{1}/restore"; - public const string CreateFolder = @"v1/folder/"; - public const string DeleteFolder = @"v1/folder/"; - public const string CopyFolder = @"v1/bulkJobs/copyFolder"; - public const string MoveFolder = @"v1/bulkJobs/moveFolder"; - public const string GetJobStatus = @"v1/bulkJobs/{0}"; - public const string GetFileVersion = @"v1/files/{0}/versions"; - public const string GetFileVersionDetail = @"v1/files/{0}/versions/{1}"; - public const string GetBoundaryString = "ImageKit-dLV9Wyq26L"; - } -} \ No newline at end of file diff --git a/Imagekit/Constant/errorMessages.cs b/Imagekit/Constant/errorMessages.cs deleted file mode 100644 index 3e24fa51..00000000 --- a/Imagekit/Constant/errorMessages.cs +++ /dev/null @@ -1,73 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit.Constant -{ - public static class ErrorMessages - { - public const string MandatoryInitializationMissing = "{ \"message\": \"Missing publicKey or privateKey or urlEndpoint during Imagekit initialization\", \"help\": \"\" }"; - public const string MandatoryPublicKeyMissing = "{ \"message\": \"Missing publicKey during Imagekit initialization\", \"help\": \"\" }"; - public const string PrivateKeyMissing = "{ \"message\": \"Missing privateKey during Imagekit initialization\", \"help\": \"\" }"; - public const string MandatoryUrlEndpointKeyMissing = "{ \"message\": \"Missing urlEndpoint during Imagekit initialization\", \"help\": \"\" }"; - public const string InvalidTransformationPosition = "{ \"message\": \"Invalid transformationPosition parameter\", \"help\": \"\" }"; - public const string CachePurgeUrlMissing = "{ \"message\": \"Missing URL parameter for this request\", \"help\": \"\" }"; - public const string CachePurgeStatusIdMissing = "{ message: \"Missing Request ID parameter for this request\", \"help\": \"\" }"; - public const string FileIdMissing = "{ \"message\": \"Missing File ID parameter for this request\", \"help\": \"\" }"; - public const string InvalidUri = "{ \"message\": \"Invalid URI\", \"help\": \"Only HTTP or HTTPS Type Absolute URL are valid.\" }"; - public const string UpdateDataMissing = "{ \"message\": \"Missing file update data for this request\", \"help\": \"\" }"; - public const string UpdateDataTagsInvalid = "{ \"message\": \"Invalid tags parameter for this request\", \"help\": \"tags should be passed as 'null', a string like 'tag1,tag2', or an array like (new string[] { 'tag1', 'tag2' })\" }"; - public const string UpdateDataCoordsInvalid = "{ \"message\": \"Invalid customCoordinates parameter for this request\", \"help\": \"customCoordinates should be passed as null or a string like 'x,y,width,height'\" }"; - public const string ListFilesInputMissing = "{ \"message\": \"Missing options for list files\", \"help\": \"If you do not want to pass any parameter for listing, pass an empty object\" }"; - public const string MissingUploadData = "{ \"message\": \"Missing data for upload\", \"help\": \"\" }"; - public const string MissingUploadFileParameter = "{ \"message\": \"Missing file parameter for upload\", \"help\": \"\" }"; - public const string InvalidPhashValue = "{ \"message: \"Invalid pHash value\", \"help\": \"Both pHash strings must be valid hexadecimal numbers\" }"; - public const string MissingPhashValue = "{ \"message: \"Missing pHash value\", \"help\": \"Please pass two pHash values\" }"; - public const string UnequalStringLength = "{ \"message: \"Unequal pHash string length\", \"help\": \"For distance calucation, the two pHash strings must have equal length\" }"; - public const string InvalidfileIdsValue = "{ \"message: \"Invalid value for fileId\", \"help\": \"fileIds should be an string array of fileId of the files to delete. The array should have atleast one fileId.\" }"; - public const string InvalidUrlValue = "{ \"message: \"Missing URL parameter for this request\", \"help\": \"\" }"; - public const string InvalidCopyValue = "{ \"message: \"sourceFilePath and DestinationFilePath both parameter are required\", \"help\": \"\" }"; - public const string InvalidJobValue = "{ \"message: \"JobId parameter are required\", \"help\": \"\" }"; - public const string InvalidFolderValue = "{ \"message: \"FilePath and NewFileName both parameter are required\", \"help\": \"\" }"; - - public const string MissingUploadFilenameParameter = "{ \"message\": \"Missing fileName parameter for upload\", \"help\": \"\" }"; - public const string InvalidMetaTagValue = "{ \"message: \"Invalid MetaData parameter for this request\", \"help\": \"\" }"; - public const string InvalidMetaTagNameValue = "{ \"message: \"Invalid MetaData name parameter for this request\", \"help\": \"\" }"; - - public const string InvalidMetaTagLabelValue = "{ \"message: \"Invalid MetaData label parameter for this request\", \"help\": \"\" }"; - public const string InvalidMetaTagSchemaValue = "{ \"message: \"Invalid MetaData Schema parameter for this request\", \"help\": \"\" }"; - public const string InvalidDelVerValue = "{ \"message: \"FieldId and versionId both parameters are required\", \"help\": \"\" }"; - - public const string InvalidFileUploadObjValue = "{ \"message: \"Missing data for upload\", \"help\": \"\" }"; - - public const string InvalidFileValue = "{ \"message: \"Missing file parameter for upload\", \"help\": \"\" }"; - public const string InvalidTagValue = "{ \"message: \"Invalid parameter for this request\", \"help\": \"\" }"; - - public const string InvalidTagParamValue = "{ \"message: \"Invalid value for tags\", \"help\": \"tags should be a non empty array of string like ['tag1', 'tag2'].\" }"; - public const string InvalidFiledParamValue = "{ \"message: \"Invalid value for fileIds\", \"help\": \"fileIds should be an array of fileId of the files. The array should have atleast one fileId.\" }"; - - public const string InvalidMetaTagIdValue = "{ \"message: \"Invalid MetaData Id parameter for this request\", \"help\": \"\" }"; - - public const string InvalidFieldIdDelVerValue = "{ \"message: \"Missing fileId parameter for this request\", \"help\": \"\" }"; - public const string InvalidversionIdDelVerValue = "{ \"message: \"Missing versionId parameter for this request\", \"help\": \"\" }"; - public const string InvalidMoveValue = "{ \"message: \"sourceFilePath and DestinationFilePath both parameter are required\", \"help\": \"\" }"; - public const string InvalidSourceValue = "{ \"message: \"Missing sourceFilePath parameter for this request\", \"help\": \"\" }"; - public const string InvalidDestinationValue = "{ \"message: \"Missing DestinationFilePath parameter for this request\", \"help\": \"\" }"; - public const string InvalidDelFolderValue = "{ \"message: \"FolderPath parameter are required\", \"help\": \"\" }"; - - public const string InvalidCreateFolderValue = "{ \"message: \"Missing folderName and FolderPath parameters\", \"help\": \"\" }"; - public const string InvalidfolderNameValue = "{ \"message: \"Missing folderName parameters\", \"help\": \"\" }"; - public const string InvalidFolderPathValue = "{ \"message: \"Missing FolderPath parameters\", \"help\": \"\" }"; - - public const string InvalidCopyFolderValue = "{ \"message: \"Missing folderName and FolderPath parameters\", \"help\": \"\" }"; - public const string InvalidCopysourceFolderPathValue = "{ \"message: \"Missing sourceFolderPath parameters\", \"help\": \"\" }"; - public const string InvalidCopydestinationPathValue = "{ \"message: \"Missing destinationPath parameters\", \"help\": \"\" }"; - - public const string InvalidRenameValue = "{ \"message: \"FilePath and NewFileName both parameters are required\", \"help\": \"\" }"; - public const string InvalidRenameFilePathValue = "{ \"message: \"Missing FilePath parameters\", \"help\": \"\" }"; - public const string InvalidRenameNewFileNameValue = "{ \"message: \"Missing NewFileName parameters\", \"help\": \"\" }"; - public const string InvalidKey = "{ \"message: \"Missing API Key parameters\", \"help\": \"\" }"; - public const string InvalidApiUrl = "{ \"message: \"Missing API URL parameters\", \"help\": \"\" }"; - public const string InvalidPurgeUrl = "{ \"message: \"Missing Purge Request URL parameters\", \"help\": \"\" }"; - } -} diff --git a/Imagekit/GlobalSuppressions.cs b/Imagekit/GlobalSuppressions.cs deleted file mode 100644 index e8ce5786..00000000 --- a/Imagekit/GlobalSuppressions.cs +++ /dev/null @@ -1,49 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "not needed", Scope = "member", Target = "~F:Imagekit.Models.DeleteFileVersionRequest.fileId")] -[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "not needed", Scope = "member", Target = "~M:Imagekit.Sdk.RestClient.MoveFile(MoveFileRequest)~ResponseMetaData")] -[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "not needed", Scope = "member", Target = "~M:Imagekit.Imagekit.#ctor(System.String,System.String,System.String,System.String)")] -[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1601:Partial elements should be documented", Justification = "not needed", Scope = "type", Target = "~T:Imagekit.BaseImagekit`1")] -[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "not needed", Scope = "type", Target = "~T:ImagekitSample.Program")] -[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1117:Parameters should be on same line or separate lines", Justification = "not needed", Scope = "member", Target = "~M:Imagekit.Sdk.RestClient.PurgeCacheAsync(System.String)~System.Threading.Tasks.Task{ResponseMetaData}")] -[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1117:Parameters should be on same line or separate lines", Justification = "not needed", Scope = "member", Target = "~M:Imagekit.Sdk.RestClient.DeleteFileVersion(Imagekit.Models.DeleteFileVersionRequest)~ResponseMetaData")] -[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "not needed", Scope = "type", Target = "~T:Imagekit.Helper.QueryMaker")] -[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "not needed", Scope = "member", Target = "~M:Imagekit.Sdk.ImagekitClient.GetRemoteFileMetadata(System.String)~ResponseMetaData")] -[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1117:Parameters should be on same line or separate lines", Justification = "not needed", Scope = "member", Target = "~M:Imagekit.Sdk.RestClient.DeleteFileVersionAsync(Imagekit.Models.DeleteFileVersionRequest)~System.Threading.Tasks.Task{ResponseMetaData}")] -[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "not needed", Scope = "member", Target = "~M:Imagekit.Sdk.RestClient.PurgeStatus(System.String)~ResponseMetaData")] -[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "not needed", Scope = "member", Target = "~F:Imagekit.BaseImagekit`1.options")] -[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "not needed", Scope = "member", Target = "~F:Transformation.nestedTransforms")] -[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "not needed", Scope = "member", Target = "~F:Transformation.transformParams")] -[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "not needed", Scope = "member", Target = "~F:Url.options")] -[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:Elements should appear in the correct order", Justification = "not needed", Scope = "member", Target = "~F:Transformation.transformParams")] -[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:Elements should appear in the correct order", Justification = "not needed", Scope = "member", Target = "~M:Imagekit.Sdk.ImagekitClient.#ctor(System.String,System.String,System.String)")] -[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:Elements should appear in the correct order", Justification = "not needed", Scope = "member", Target = "~M:Transformation.#ctor")] -[assembly: SuppressMessage("Compiler", "CS0108:Member hides inherited member; missing new keyword", Justification = "not needed", Scope = "member", Target = "~P:Imagekit.Models.Response.ResultMetaData.Raw")] -[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "not needed", Scope = "member", Target = "~P:ResultCache.Help")] -[assembly: SuppressMessage("Compiler", "CS0108:Member hides inherited member; missing new keyword", Justification = "not needed", Scope = "member", Target = "~P:ResultCache.Raw")] -[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "not needed", Scope = "member", Target = "~P:ResultCache.Raw")] -[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "not needed", Scope = "member", Target = "~P:ResultCache.RequestId")] -[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "not needed", Scope = "member", Target = "~P:ResultCacheStatus.Help")] -[assembly: SuppressMessage("Compiler", "CS0108:Member hides inherited member; missing new keyword", Justification = "not needed", Scope = "member", Target = "~P:ResultCacheStatus.Raw")] -[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "not needed", Scope = "member", Target = "~P:ResultCacheStatus.Raw")] -[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "not needed", Scope = "member", Target = "~P:ResultCacheStatus.Status")] -[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "not needed", Scope = "member", Target = "~P:ResultCustomMetaDataField.Id")] -[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "not needed", Scope = "member", Target = "~P:ResultCustomMetaDataField.Label")] -[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "not needed", Scope = "member", Target = "~P:ResultCustomMetaDataField.Message")] -[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "not needed", Scope = "member", Target = "~P:ResultCustomMetaDataField.Name")] -[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "not needed", Scope = "member", Target = "~P:ResultCustomMetaDataField.Schema")] -[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "not needed", Scope = "member", Target = "~P:ResultException.Help")] -[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "not needed", Scope = "member", Target = "~P:ResultException.Message")] -[assembly: SuppressMessage("Compiler", "CS0108:Member hides inherited member; missing new keyword", Justification = "not needed", Scope = "member", Target = "~P:ResultFileDelete.Raw")] -[assembly: SuppressMessage("Compiler", "CS0108:Member hides inherited member; missing new keyword", Justification = "not needed", Scope = "member", Target = "~P:ResultList.Raw")] -[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "not needed", Scope = "member", Target = "~P:ResultNoContent.ResponseMetaData")] -[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "not needed", Scope = "member", Target = "~M:Imagekit.Helper.GetJsonBody.GetBase64(System.Byte[])~System.String")] -[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "not needed", Scope = "member", Target = "~M:Transformation.ToString~System.String")] -[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1123:Do not place regions within elements", Justification = "not needed", Scope = "member", Target = "~M:ImagekitSample.Program.Main(System.String[])")] -[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1400:Access modifier should be declared", Justification = "not needed", Scope = "member", Target = "~M:ImagekitSample.Program.Main(System.String[])")] -[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "not needed", Scope = "type", Target = "~T:Imagekit.Sdk.ImagekitClient")] -[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static elements should appear before instance elements", Justification = "not needed", Scope = "member", Target = "~M:Transformation.ToString(System.Object)~System.String")] diff --git a/Imagekit/Helper/GetJsonBody.cs b/Imagekit/Helper/GetJsonBody.cs deleted file mode 100644 index e7f4667e..00000000 --- a/Imagekit/Helper/GetJsonBody.cs +++ /dev/null @@ -1,423 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit.Helper -{ - using System; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.IO; - using System.Runtime.Serialization.Formatters.Binary; - using global::Imagekit.Models; - - [ExcludeFromCodeCoverage] - public static class GetJsonBody - { - public static string CreateCustomMetaDataBody(CustomMetaDataFieldCreateRequest customMetaDataFieldCreateRequest) - { - string body = string.Empty; - if (customMetaDataFieldCreateRequest.schema.isValueRequired) - { - if (customMetaDataFieldCreateRequest.schema.type == "Text") - { - body = @"{" + "\n" + - @" ""name"": " + AddDoubleQuotes(customMetaDataFieldCreateRequest.name) + "," + "\n" + - @" ""label"": " + AddDoubleQuotes(customMetaDataFieldCreateRequest.label) + "," + "\n" + - @" ""schema"": {" + "\n" + - @" ""type"": " + - AddDoubleQuotes(customMetaDataFieldCreateRequest.schema.type.ToString()) + "," + "\n" + - @" ""minLength"": " + - customMetaDataFieldCreateRequest.schema.minLength.ToString() + "," + "\n" + - @" ""maxLength"": " + - customMetaDataFieldCreateRequest.schema.maxLength.ToString() + "," + "\n" + - @" ""isValueRequired"": " + - customMetaDataFieldCreateRequest.schema.isValueRequired.ToString().ToLower() + "," + "\n" + - @" ""defaultValue"": " + - AddDoubleQuotesObject(customMetaDataFieldCreateRequest.schema.defaultValue) + "\n" + - @" }" + "\n" + - @"}"; - } - - if (customMetaDataFieldCreateRequest.schema.type == "Textarea") - { - body = @"{" + "\n" + - @" ""name"": " + AddDoubleQuotes(customMetaDataFieldCreateRequest.name) + "," + "\n" + - @" ""label"": " + AddDoubleQuotes(customMetaDataFieldCreateRequest.label) + "," + "\n" + - @" ""schema"": {" + "\n" + - @" ""type"": " + - AddDoubleQuotes(customMetaDataFieldCreateRequest.schema.type.ToString()) + "," + "\n" + - @" ""minLength"": " + - customMetaDataFieldCreateRequest.schema.minLength.ToString() + "," + "\n" + - @" ""maxLength"": " + - customMetaDataFieldCreateRequest.schema.maxLength.ToString() + "," + "\n" + - @" ""isValueRequired"": " + - customMetaDataFieldCreateRequest.schema.isValueRequired.ToString().ToLower() + "," + "\n" + - @" ""defaultValue"": " + - AddDoubleQuotesObject(customMetaDataFieldCreateRequest.schema.defaultValue) + "\n" + - @" }" + "\n" + - @"}"; - } - - if (customMetaDataFieldCreateRequest.schema.type == "Number") - { - body = @"{" + "\n" + - @" ""name"": " + AddDoubleQuotes(customMetaDataFieldCreateRequest.name) + "," + "\n" + - @" ""label"": " + AddDoubleQuotes(customMetaDataFieldCreateRequest.label) + "," + "\n" + - @" ""schema"": {" + "\n" + - @" ""type"": " + - AddDoubleQuotes(customMetaDataFieldCreateRequest.schema.type.ToString()) + "," + "\n" + - @" ""minValue"": " + - customMetaDataFieldCreateRequest.schema.minValue.ToString() + "," + "\n" + - @" ""maxValue"": " + - customMetaDataFieldCreateRequest.schema.maxValue.ToString() + "," + "\n" + - @" ""isValueRequired"": " + - customMetaDataFieldCreateRequest.schema.isValueRequired.ToString().ToLower() + "," + "\n" + - @" ""defaultValue"": " + - customMetaDataFieldCreateRequest.schema.defaultValue + "\n" + - - @" }" + "\n" + - @"}"; - } - - if (customMetaDataFieldCreateRequest.schema.type == "Date") - { - body = @"{" + "\n" + - @" ""name"": " + AddDoubleQuotes(customMetaDataFieldCreateRequest.name) + "," + "\n" + - @" ""label"": " + AddDoubleQuotes(customMetaDataFieldCreateRequest.label) + "," + "\n" + - @" ""schema"": {" + "\n" + - @" ""type"": " + - AddDoubleQuotes(customMetaDataFieldCreateRequest.schema.type.ToString()) + "," + "\n" + - @" ""minValue"": " + - AddDoubleQuotes(customMetaDataFieldCreateRequest.schema.minValue.ToString()) + "," + "\n" + - @" ""maxValue"": " + - AddDoubleQuotes(customMetaDataFieldCreateRequest.schema.maxValue.ToString()) + "," + "\n" + - @" ""isValueRequired"": " + - customMetaDataFieldCreateRequest.schema.isValueRequired.ToString().ToLower() + "," + "\n" + - @" ""defaultValue"": " + - AddDoubleQuotesObject(customMetaDataFieldCreateRequest.schema.defaultValue) + "\n" + - @" }" + "\n" + - @"}"; - } - - if (customMetaDataFieldCreateRequest.schema.type == "SingleSelect") - { - body = @"{" + "\n" + - @" ""name"": " + AddDoubleQuotes(customMetaDataFieldCreateRequest.name) + "," + "\n" + - @" ""label"": " + AddDoubleQuotes(customMetaDataFieldCreateRequest.label) + "," + "\n" + - @" ""schema"": {" + "\n" + - @" ""type"": " + - AddDoubleQuotes(customMetaDataFieldCreateRequest.schema.type.ToString()) + "," + "\n" + - @" ""selectOptions"": " + - AddBigQuotes(customMetaDataFieldCreateRequest.schema.selectOptions) + "," + "\n" + - @" ""isValueRequired"": " + - customMetaDataFieldCreateRequest.schema.isValueRequired.ToString().ToLower() + "," + "\n" + - @" ""defaultValue"": " + - AddDoubleQuotesObject(customMetaDataFieldCreateRequest.schema.defaultValue) + "\n" + - - @" }" + "\n" + - @"}"; - } - - if (customMetaDataFieldCreateRequest.schema.type == "MultiSelect") - { - body = @"{" + "\n" + - @" ""name"": " + AddDoubleQuotes(customMetaDataFieldCreateRequest.name) + "," + "\n" + - @" ""label"": " + AddDoubleQuotes(customMetaDataFieldCreateRequest.label) + "," + "\n" + - @" ""schema"": {" + "\n" + - @" ""type"": " + - AddDoubleQuotes(customMetaDataFieldCreateRequest.schema.type.ToString()) + "," + "\n" + - @" ""selectOptions"": " + - AddBigQuotes(customMetaDataFieldCreateRequest.schema.selectOptions) + "," + "\n" + - @" ""isValueRequired"": " + - customMetaDataFieldCreateRequest.schema.isValueRequired.ToString().ToLower() + "," + "\n" + - @" ""defaultValue"": " + - customMetaDataFieldCreateRequest.schema.defaultValue + "\n" + - - @" }" + "\n" + - @"}"; - } - } - else - { - if (customMetaDataFieldCreateRequest.schema.type == "Text") - { - body = @"{" + "\n" + - @" ""name"": " + AddDoubleQuotes(customMetaDataFieldCreateRequest.name) + "," + "\n" + - @" ""label"": " + AddDoubleQuotes(customMetaDataFieldCreateRequest.label) + "," + "\n" + - @" ""schema"": {" + "\n" + - @" ""type"": " + - AddDoubleQuotes(customMetaDataFieldCreateRequest.schema.type.ToString()) + "," + "\n" + - @" ""minLength"": " + - customMetaDataFieldCreateRequest.schema.minLength.ToString() + "," + "\n" + - @" ""maxLength"": " + - customMetaDataFieldCreateRequest.schema.maxLength.ToString() + "," + "\n" + - @" ""isValueRequired"": " + - customMetaDataFieldCreateRequest.schema.isValueRequired.ToString().ToLower() + "\n" + - @" }" + "\n" + - @"}"; - } - - if (customMetaDataFieldCreateRequest.schema.type == "Textarea") - { - body = @"{" + "\n" + - @" ""name"": " + AddDoubleQuotes(customMetaDataFieldCreateRequest.name) + "," + "\n" + - @" ""label"": " + AddDoubleQuotes(customMetaDataFieldCreateRequest.label) + "," + "\n" + - @" ""schema"": {" + "\n" + - @" ""type"": " + - AddDoubleQuotes(customMetaDataFieldCreateRequest.schema.type.ToString()) + "," + "\n" + - @" ""minLength"": " + - customMetaDataFieldCreateRequest.schema.minLength.ToString() + "," + "\n" + - @" ""maxLength"": " + - customMetaDataFieldCreateRequest.schema.maxLength.ToString() + "," + "\n" + - @" ""isValueRequired"": " + - customMetaDataFieldCreateRequest.schema.isValueRequired.ToString().ToLower() + "\n" + - - @" }" + "\n" + - @"}"; - } - - if (customMetaDataFieldCreateRequest.schema.type == "Number") - { - body = @"{" + "\n" + - @" ""name"": " + AddDoubleQuotes(customMetaDataFieldCreateRequest.name) + "," + "\n" + - @" ""label"": " + AddDoubleQuotes(customMetaDataFieldCreateRequest.label) + "," + "\n" + - @" ""schema"": {" + "\n" + - @" ""type"": " + - AddDoubleQuotes(customMetaDataFieldCreateRequest.schema.type.ToString()) + "," + "\n" + - @" ""minValue"": " + - customMetaDataFieldCreateRequest.schema.minValue.ToString() + "," + "\n" + - @" ""maxValue"": " + - customMetaDataFieldCreateRequest.schema.maxValue.ToString() + "," + "\n" + - @" ""isValueRequired"": " + - customMetaDataFieldCreateRequest.schema.isValueRequired.ToString().ToLower() + "\n" + - @" }" + "\n" + - @"}"; - } - - if (customMetaDataFieldCreateRequest.schema.type == "Date") - { - body = @"{" + "\n" + - @" ""name"": " + AddDoubleQuotes(customMetaDataFieldCreateRequest.name) + "," + "\n" + - @" ""label"": " + AddDoubleQuotes(customMetaDataFieldCreateRequest.label) + "," + "\n" + - @" ""schema"": {" + "\n" + - @" ""type"": " + - AddDoubleQuotes(customMetaDataFieldCreateRequest.schema.type.ToString()) + "," + "\n" + - @" ""minValue"": " + - AddDoubleQuotes(customMetaDataFieldCreateRequest.schema.minValue.ToString()) + "," + "\n" + - @" ""maxValue"": " + - AddDoubleQuotes(customMetaDataFieldCreateRequest.schema.maxValue.ToString()) + "," + "\n" + - @" ""isValueRequired"": " + - customMetaDataFieldCreateRequest.schema.isValueRequired.ToString().ToLower() + "\n" + - @" }" + "\n" + - @"}"; - } - - if (customMetaDataFieldCreateRequest.schema.type == "SingleSelect" || - customMetaDataFieldCreateRequest.schema.type == "MultiSelect") - { - body = @"{" + "\n" + - @" ""name"": " + AddDoubleQuotes(customMetaDataFieldCreateRequest.name) + "," + "\n" + - @" ""label"": " + AddDoubleQuotes(customMetaDataFieldCreateRequest.label) + "," + "\n" + - @" ""schema"": {" + "\n" + - @" ""type"": " + - AddDoubleQuotes(customMetaDataFieldCreateRequest.schema.type.ToString()) + "," + "\n" + - @" ""selectOptions"": " + - AddBigQuotes(customMetaDataFieldCreateRequest.schema.selectOptions) + "," + "\n" + - @" ""isValueRequired"": " + - customMetaDataFieldCreateRequest.schema.isValueRequired.ToString().ToLower() + "\n" + - @" }" + "\n" + - @"}"; - } - } - - return body; - } - - public static string UpdateCustomMetaDataBody(CustomMetaDataFieldUpdateRequest customMetaDataFieldCreateRequest) - { - string body = string.Empty; - if (customMetaDataFieldCreateRequest.schema.type == "Text") - { - body = @"{" + "\n" + - @" ""schema"": {" + "\n" + - @" ""type"": " + - AddDoubleQuotes(customMetaDataFieldCreateRequest.schema.type.ToString()) + "," + "\n" + - @" ""minLength"": " + - customMetaDataFieldCreateRequest.schema.minLength.ToString() + "," + "\n" + - @" ""maxLength"": " + - customMetaDataFieldCreateRequest.schema.maxLength.ToString() + "\n" + - @" }" + "\n" + - @"}"; - } - - if (customMetaDataFieldCreateRequest.schema.type == "Textarea") - { - body = @"{" + "\n" + - @" ""schema"": {" + "\n" + - @" ""type"": " + - AddDoubleQuotes(customMetaDataFieldCreateRequest.schema.type.ToString()) + "," + "\n" + - @" ""minLength"": " + - customMetaDataFieldCreateRequest.schema.minLength.ToString() + "," + "\n" + - @" ""maxLength"": " + - customMetaDataFieldCreateRequest.schema.maxLength.ToString() + "\n" + - @" }" + "\n" + - @"}"; - } - - if (customMetaDataFieldCreateRequest.schema.type == "Number") - { - body = @"{" + "\n" + - @" ""schema"": {" + "\n" + - @" ""type"": " + - AddDoubleQuotes(customMetaDataFieldCreateRequest.schema.type.ToString()) + "," + "\n" + - @" ""minValue"": " + - customMetaDataFieldCreateRequest.schema.minValue.ToString() + "," + "\n" + - @" ""maxValue"": " + - customMetaDataFieldCreateRequest.schema.maxValue.ToString() + "\n" + - @" }" + "\n" + - @"}"; - } - - if (customMetaDataFieldCreateRequest.schema.type == "Date") - { - body = @"{" + "\n" + - @" ""schema"": {" + "\n" + - @" ""type"": " + - AddDoubleQuotes(customMetaDataFieldCreateRequest.schema.type.ToString()) + "," + "\n" + - @" ""minValue"": " + - customMetaDataFieldCreateRequest.schema.minValue.ToString() + "," + "\n" + - @" ""maxValue"": " + - customMetaDataFieldCreateRequest.schema.maxValue.ToString() + "\n" + - @" }" + "\n" + - @"}"; - } - - if (customMetaDataFieldCreateRequest.schema.type == "SingleSelect") - { - body = @"{" + "\n" + - @" ""schema"": {" + "\n" + - @" ""type"": " + - AddDoubleQuotes(customMetaDataFieldCreateRequest.schema.type.ToString()) + "," + "\n" + - @" ""selectOptions"": " + AddBigQuotes(customMetaDataFieldCreateRequest.schema.selectOptions) + "\n" + - - @" }" + "\n" + - @"}"; - } - - return body; - } - - public static string DeleteFolderBody(DeleteFolderRequest deleteFolderRequest) - { - var body = @"{" + "\n" + - @" ""folderPath"" : " + AddDoubleQuotes(deleteFolderRequest.folderPath) + "\n" + - @"}"; - - return body; - } - - private static string AddDoubleQuotes(this string value) - { - return "\"" + value + "\""; - } - - private static string AddDoubleQuotesObject(this object value) - { - return "\"" + value + "\""; - } - - private static string AddBigQuotes(this string[] value) - { - var joinedNames = "\"" + string.Join("\", \"", value) + "\""; - - return "[" + joinedNames + "]"; - } - - public static string GetBase64(object imageArray) - { - string base64ImageRepresentation = Convert.ToBase64String((byte[])imageArray); - return base64ImageRepresentation; - } - - public static string GetBase64Uri(string imagePath) - { - var uri = new System.Uri(imagePath); - byte[] imageArray = System.IO.File.ReadAllBytes(uri.AbsolutePath); - string base64ImageRepresentation = Convert.ToBase64String(imageArray); - return base64ImageRepresentation; - } - - public static string GetFileRequestBody(GetFileListRequest getFileListRequest) - { - QueryMaker queryMaker = new QueryMaker(); - Dictionary options = new Dictionary(); - - if (getFileListRequest.Sort != null) - { - options.Add("sort", getFileListRequest.Sort); - } - - if (getFileListRequest.Path != null) - { - options.Add("path", getFileListRequest.Path); - } - - if (getFileListRequest.SearchQuery != null) - { - options.Add("searchQuery", getFileListRequest.SearchQuery); - } - - if (getFileListRequest.FileType != null) - { - options.Add("fileType", getFileListRequest.FileType); - } - - if (getFileListRequest.Limit > 0) - { - options.Add("limit", getFileListRequest.Limit.ToString()); - } - - if (getFileListRequest.Skip > 0) - { - options.Add("skip", getFileListRequest.Skip.ToString()); - } - - if (getFileListRequest.Name != null) - { - options.Add("name", getFileListRequest.Name.ToString()); - } - - foreach (KeyValuePair entry in options) - { - queryMaker.Add(string.Format("{0}={1}", entry.Key, entry.Value)); - } - - return queryMaker.Get(); - } - } - - public class QueryMaker - { - private string query; - - public void Add(string q) - { - if (this.query != null) - { - this.query += "&"; - } - else - { - this.query = string.Empty; - } - - this.query += q; - } - - public string Get() - { - return this.query; - } - } -} diff --git a/Imagekit/Helper/ImageKitException.cs b/Imagekit/Helper/ImageKitException.cs deleted file mode 100644 index 48f1e504..00000000 --- a/Imagekit/Helper/ImageKitException.cs +++ /dev/null @@ -1,26 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit.Helper -{ - using System; - - public class ImagekitException : Exception - { - public ImagekitException() - { - } - - public ImagekitException(string message) - : base(message) - { - throw new Exception(message); - } - - public ImagekitException(string message, Exception inner) - : base(message, inner) - { - } - } -} \ No newline at end of file diff --git a/Imagekit/Helper/ImagekitBase.cs b/Imagekit/Helper/ImagekitBase.cs deleted file mode 100644 index ef673522..00000000 --- a/Imagekit/Helper/ImagekitBase.cs +++ /dev/null @@ -1,83 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit -{ - using System; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.Text.RegularExpressions; - - [ExcludeFromCodeCoverage] - public abstract partial class BaseImagekit - where T : BaseImagekit - { - public Dictionary options = new Dictionary(); - - public BaseImagekit(string publicKey, string urlEndpoint, string transformationPosition = "path") - { - if (string.IsNullOrEmpty(publicKey)) - { - throw new ArgumentNullException(nameof(publicKey)); - } - - if (string.IsNullOrEmpty(urlEndpoint)) - { - throw new ArgumentNullException(nameof(urlEndpoint)); - } - - Regex rgx = new Regex("^(path|query)$"); - if (transformationPosition == null || !rgx.IsMatch(transformationPosition)) - { - throw new Exception(Constant.ErrorMessages.InvalidTransformationPosition); - } - - this.Add("publicKey", publicKey); - this.Add("urlEndpoint", urlEndpoint); - this.Add("transformationPosition", transformationPosition); - } - - public string Generate() - { - Transformation transformation = (Transformation)this.options["transformation"]; - string tranformationString = transformation.Generate(); - return new Url(this.options).UrlBuilder(tranformationString); - } - } - - public class ServerImagekit : BaseImagekit - { - public ServerImagekit( - string publicKey, - string privateKey, - string urlEndpoint, - string transformationPosition = "path") - : base(publicKey, urlEndpoint, transformationPosition) - { - if (string.IsNullOrEmpty(privateKey)) - { - throw new ArgumentNullException(nameof(privateKey)); - } - - this.Add("privateKey", privateKey); - } - } - - // Leaving this for backwards compatibility. - // Renaming the class: - // a) solves the issue where Imagekit must always be fully qualified since the name is the same as the assembly - // b) allows for clarification between a server-side imagekit and a client-side imagekit - [Obsolete("Use ServerImagekit")] - public class Imagekit : ServerImagekit - { - public Imagekit( - string publicKey, - string privateKey, - string urlEndpoint, - string transformationPosition = "path") - : base(publicKey, privateKey, urlEndpoint, transformationPosition) - { - } - } -} diff --git a/Imagekit/Helper/ImagekitParams.cs b/Imagekit/Helper/ImagekitParams.cs deleted file mode 100644 index 38b6f937..00000000 --- a/Imagekit/Helper/ImagekitParams.cs +++ /dev/null @@ -1,73 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit -{ - public partial class BaseImagekit - { - public T Path(string value) - { - return this.Add("path", value); - } - - public T Src(string value) - { - return this.Add("src", value); - } - - public T UrlEndpoint(string value) - { - return this.Add("urlEndpoint", value); - } - - public T Url(Transformation value) - { - return this.Add("transformation", value); - } - - public T QueryParameters(params string[] value) - { - return this.Add("queryParameters", value); - } - - public T TransformationPosition(string value) - { - return this.Add("transformationPosition", value); - } - - public T Signed(bool value = true) - { - return this.Add("signed", value); - } - - public T ExpireSeconds(int value) - { - return this.Add("expireSeconds", value); - } - - /// - /// Add transformation parameter. - /// - /// The name. - /// The value. - public T Add(string key, object value) - { - if (this.options.ContainsKey(key)) - { - this.options[key] = value; - if (key == "transformation") - { - this.options.Remove("path"); - this.options.Remove("src"); - } - } - else - { - this.options.Add(key, value); - } - - return (T)this; - } - } -} diff --git a/Imagekit/Helper/MultipartFormDataModel.cs b/Imagekit/Helper/MultipartFormDataModel.cs deleted file mode 100644 index cb670a75..00000000 --- a/Imagekit/Helper/MultipartFormDataModel.cs +++ /dev/null @@ -1,180 +0,0 @@ -namespace Imagekit.Helper -{ - using System; - using System.Net; - using System.Net.Http; - using System.Net.Http.Headers; - using global::Imagekit.Constant; - using global::Imagekit.Models; - using global::Imagekit.Util; - using Newtonsoft.Json; - using System.Collections.Generic; - - public static class MultipartFormDataModel - { - private static string boundary = UrlHandler.GetBoundaryString; - - public static MultipartFormDataContent Build(FileCreateRequest fileCreateRequest) - { - HttpContent content = new StringContent(string.Empty); - MultipartFormDataContent formdata = new MultipartFormDataContent(boundary); - formdata.Headers.Remove("Content-Type"); - formdata.Headers.TryAddWithoutValidation("Content-Type", "multipart/form-data; boundary=" + boundary); - formdata.Add(new StringContent(fileCreateRequest.fileName), "fileName"); - if (fileCreateRequest.file.GetType().Name == "Byte[]") - { - formdata.Add(new StringContent(GetJsonBody.GetBase64(fileCreateRequest.file)), "file"); - } - else - { - formdata.Add(new StringContent(fileCreateRequest.file.ToString()), "file"); - } - - if (fileCreateRequest.useUniqueFileName) - { - formdata.Add(new StringContent("true"), "useUniqueFileName"); - } - else - { - formdata.Add(new StringContent("false"), "useUniqueFileName"); - } - - if (fileCreateRequest.tags != null) - { - formdata.Add(new StringContent(Utils.ListToString(fileCreateRequest.tags)), "tags"); - } - - if (!string.IsNullOrEmpty(fileCreateRequest.folder)) - { - formdata.Add(new StringContent(fileCreateRequest.folder), "folder"); - } - - if (fileCreateRequest.isPrivateFile) - { - formdata.Add(new StringContent("true"), "isPrivateFile"); - } - - if (!string.IsNullOrEmpty(fileCreateRequest.customCoordinates)) - { - formdata.Add(new StringContent(fileCreateRequest.customCoordinates), "customCoordinates"); - } - - if (fileCreateRequest.responseFields != null) - { - formdata.Add(new StringContent(Utils.ListToString(fileCreateRequest.responseFields)), "responseFields"); - } - - if (fileCreateRequest.overwriteFile) - { - formdata.Add(new StringContent("true"), "overwriteFile"); - } - - if (fileCreateRequest.overwriteAITags) - { - formdata.Add(new StringContent("true"), "overwriteAITags"); - } - - if (fileCreateRequest.overwriteTags) - { - formdata.Add(new StringContent("true"), "overwriteTags"); - } - - if (fileCreateRequest.overwriteCustomMetadata) - { - formdata.Add(new StringContent("false"), "overwriteCustomMetadata"); - } - - if (fileCreateRequest.extensions != null) - { - var myContent = JsonConvert.SerializeObject( - fileCreateRequest.extensions, - new JsonSerializerSettings() - { - NullValueHandling = NullValueHandling.Ignore, - }); - formdata.Add(new StringContent(myContent), "extensions"); - } - - if (fileCreateRequest.webhookUrl != null) - { - formdata.Add(new StringContent(fileCreateRequest.webhookUrl), "webhookUrl"); - } - - if (fileCreateRequest.customMetadata != null) - { - string jsonResult = JsonConvert.SerializeObject(fileCreateRequest.customMetadata); - formdata.Add(new StringContent(jsonResult), "customMetadata"); - } - - if (fileCreateRequest.transformation != null) - { - string jsonResult = JsonConvert.SerializeObject(fileCreateRequest.transformation); - formdata.Add(new StringContent(jsonResult), "transformation"); - } - - if (fileCreateRequest.checks != null) - { - formdata.Add(new StringContent(fileCreateRequest.checks), "checks"); - } - - if (fileCreateRequest.isPublished.HasValue) - { - formdata.Add(new StringContent(fileCreateRequest.isPublished.Value.ToString().ToLower()), "isPublished"); - } - - return formdata; - } - - public static MultipartFormDataContent BuildUpdateFile(FileUpdateRequest fileCreateRequest) - { - MultipartFormDataContent formdata = new MultipartFormDataContent(boundary); - formdata.Headers.Remove("Content-Type"); - formdata.Headers.TryAddWithoutValidation("Content-Type", "multipart/form-data; boundary=" + boundary); - - if (fileCreateRequest.tags != null) - { - formdata.Add(new StringContent(Utils.ListToString(fileCreateRequest.tags)), "tags"); - } - - if (fileCreateRequest.removeAITags != null) - { - formdata.Add(new StringContent(Utils.ListToString(fileCreateRequest.removeAITags)), "removeAITags"); - } - - if (!string.IsNullOrEmpty(fileCreateRequest.customCoordinates)) - { - formdata.Add(new StringContent(fileCreateRequest.customCoordinates), "customCoordinates"); - } - - if (fileCreateRequest.extensions != null) - { - var myContent = JsonConvert.SerializeObject( - fileCreateRequest.extensions, - new JsonSerializerSettings() - { - NullValueHandling = NullValueHandling.Ignore, - }); - formdata.Add(new StringContent(myContent), "extensions"); - } - - if (fileCreateRequest.webhookUrl != null) - { - formdata.Add(new StringContent(fileCreateRequest.webhookUrl), "webhookUrl"); - } - - if (fileCreateRequest.customMetadata != null) - { - string jSONresult = JsonConvert.SerializeObject(fileCreateRequest.customMetadata); - formdata.Add(new StringContent(jSONresult), "customMetadata"); - } - - if (fileCreateRequest.publish != null) - { - string jSONresult = JsonConvert.SerializeObject(fileCreateRequest.publish); - formdata.Add(new StringContent(jSONresult), "publish"); - } - - return formdata; - } - } -} diff --git a/Imagekit/Helper/Transformation.cs b/Imagekit/Helper/Transformation.cs deleted file mode 100644 index cc5fa234..00000000 --- a/Imagekit/Helper/Transformation.cs +++ /dev/null @@ -1,281 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; - -public partial class Transformation -{ - public const string KeyNameRegex = "^\\$[a-zA-Z][a-zA-Z0-9]*$"; - public const string ChainTransformDelimiter = ":"; - public const string TransformDelimiter = ","; - public const string TransformKeyValueDelimiter = "-"; - - /// - /// Initializes a new instance of the class. - /// - /// - /// - public Transformation(Dictionary transformParams) - { - foreach (var key in transformParams.Keys) - { - transformParams.Add(key, transformParams[key]); - } - } - - /// - /// Initializes a new instance of the class. - /// Creates transformation object initialized with array of transformation parameters. - /// - /// List of transformation parameters represented as pairs 'name=value'. - public Transformation(params string[] transformParams) - { - foreach (var pair in transformParams) - { - string[] splittedPair = pair.Split('='); - if (splittedPair.Length != 2) - { - throw new Exception(string.Format("Couldn't parse '{0}'!", pair)); - } - - this.Add(splittedPair[0], splittedPair[1]); - } - } - - /// - /// Add transformation parameter. - /// - /// The name. - /// The value. - public Transformation Add(string key, object value) - { - if (this.transformParams.ContainsKey(key)) - { - this.transformParams[key] = value; - } - else - { - this.transformParams.Add(key, value); - } - - return this; - } - - /// - /// Initializes a new instance of the class. - /// Creates empty transformation object. - /// - public Transformation() - { - } - - /// - /// A dictionary of transformation parameters. - /// - protected Dictionary transformParams = new Dictionary(); - - /// - /// A list of nested transformations. - /// - protected List nestedTransforms = new List(); - - /// - /// Initializes a new instance of the class. - /// Creates transformation object chained with other transformations. - /// - /// List of transformations to chain with. - public Transformation(List transforms) - { - if (transforms != null) - { - this.nestedTransforms = transforms; - } - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// - public Transformation(Dictionary[] dictionary) - { - for (int i = 0; i < dictionary.Length; i++) - { - if (i == dictionary.Length - 1) - { - this.transformParams = dictionary[i]; - } - else - { - this.nestedTransforms.Add(new Transformation(dictionary[i])); - } - } - } - - /// - /// Get the transformation parameters dictionary. - /// - public Dictionary Params - { - get { return this.transformParams; } - } - - /// - /// Get list of nested transformations. - /// - public List NestedTransforms - { - get { return this.nestedTransforms; } - } - - /// - /// Chain transformation. - /// - public Transformation Chain() - { - Transformation nested = this.Clone(); - nested.nestedTransforms = null; - this.nestedTransforms.Add(nested); - this.transformParams = new Dictionary(); - Transformation transform = new Transformation(this.nestedTransforms); - return transform; - } - - /// - /// Get a deep cloned copy of this transformation. - /// - /// A deep cloned copy of this transformation. - public Transformation Clone() - { - Transformation t = (Transformation)this.MemberwiseClone(); - - t.transformParams = new Dictionary(); - - foreach (var key in this.transformParams.Keys) - { - var value = this.transformParams[key]; - - if (value is Array) - { - t.Add(key, ((Array)value).Clone()); - } - else if (value is string || value is ValueType) - { - t.Add(key, value); - } - else if (value is Dictionary) - { - t.Add(key, new Dictionary((Dictionary)value)); - } - else - { - throw new Exception(string.Format("Couldn't clone parameter '{0}'!", key)); - } - } - - if (this.nestedTransforms != null) - { - t.nestedTransforms = new List(); - foreach (var nestedTransform in this.nestedTransforms) - { - t.nestedTransforms.Add(nestedTransform.Clone()); - } - } - - return t; - } - - /// - /// Get this transformation represented as string. - /// - /// The transformation represented as string. - public string Generate() - { - List parts = new List(this.nestedTransforms.Select(t => t.GetTrans()).ToList()); - - var thisTransform = this.GetTrans(); - if (!string.IsNullOrEmpty(thisTransform)) - { - parts.Add(thisTransform); - } - - return string.Join(ChainTransformDelimiter, parts.ToArray()); - } - - public string GetTrans() - { - List transformations = new List(); - List varParams = new List(); - - foreach (var key in this.transformParams.Keys) - { - string val = this.GetString(this.transformParams, key); - if (string.IsNullOrEmpty(val)) - { - varParams.Add($"{key}"); - } - else - { - if (key == "oi" || key == "di") - { - val = val.TrimStart('/').TrimEnd('/'); - val = val.Replace("/", "@@"); - } - - varParams.Add($"{key}-{val}"); - } - } - - if (varParams.Count > 0) - { - transformations.Add(string.Join(TransformDelimiter, varParams)); - } - - return string.Join(ChainTransformDelimiter, transformations.ToArray()); - } - - private string GetString(Dictionary options, string key) - { - if (options.ContainsKey(key)) - { - return ToString(options[key]); - } - else - { - return null; - } - } - - private static string ToString(object obj) - { - if (obj == null) - { - return null; - } - - if (obj is string) - { - return obj.ToString(); - } - - if (obj is float || obj is double) - { - return string.Format(CultureInfo.InvariantCulture, "{0:0.0#}", obj); - } - - return string.Format(CultureInfo.InvariantCulture, "{0}", obj); - } - - /// - /// Get this transformation represented as string. - /// - /// The transformation represented as string. - public override string ToString() - { - return this.Generate(); - } -} diff --git a/Imagekit/Helper/TransformationTypes.cs b/Imagekit/Helper/TransformationTypes.cs deleted file mode 100644 index bd796928..00000000 --- a/Imagekit/Helper/TransformationTypes.cs +++ /dev/null @@ -1,268 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -using System; -using System.Diagnostics.CodeAnalysis; - -[ExcludeFromCodeCoverage] -public partial class Transformation -{ - /// Width of a transformed image - /// - public Transformation Width(int value) - { - return this.Add("w", value); - } - - /// Height of a transformed image - /// - public Transformation Height(int value) - { - return this.Add("h", value); - } - - /// Aspect Ratio of a transformed image - /// - public Transformation AspectRatio(string value) - { - return this.Add("ar", value); - } - - /// - /// JPG compression quality. 1 is the lowest quality and 100 is the highest. The default is the - /// original image's quality or 80% if not available. - /// - /// - public Transformation Quality(int value) - { - return this.Add("q", value); - } - - /// Crop Transformation. - /// - public Transformation Crop(string value) - { - return this.Add("c", value); - } - - /// Crop Mode - /// - public Transformation CropMode(string value) - { - return this.Add("cm", value); - } - - /// - /// - public Transformation X(int value) - { - return this.Add("x", this.ConvertCoordinateParam(value)); - } - - /// - /// - public Transformation Y(int value) - { - return this.Add("y", this.ConvertCoordinateParam(value)); - } - - /// - /// - public Transformation Focus(string value) - { - return this.Add("fo", value); - } - - /// - /// - public Transformation Format(string value) - { - return this.Add("f", value); - } - - /// - /// - public Transformation Radius(object value) - { - return this.Add("r", value); - } - - /// - /// - public Transformation Background(string value) - { - return this.Add("bg", value); - } - - /// - /// - public Transformation Border(string value) - { - return this.Add("b", value); - } - - /// - /// - public Transformation Rotation(object value) - { - return this.Add("rt", value); - } - - /// - /// - public Transformation Rotate(object value) - { - return this.Add("rt", value); - } - - /// - /// - public Transformation Blur(int value) - { - return this.Add("bl", value); - } - - /// Add named transformation. - /// named transformation. - public Transformation Named(string value) - { - return this.Add("n", value); - } - - /// - /// - public Transformation Progressive(bool value) - { - return this.Add("pr", value.ToString().ToLower()); - } - - /// - /// - public Transformation Lossless(bool value) - { - return this.Add("lo", value.ToString().ToLower()); - } - - /// - /// - public Transformation Trim(int value) - { - return this.Add("t", value); - } - - /// - /// - public Transformation Metadata(bool value) - { - return this.Add("md", value.ToString().ToLower()); - } - - /// - /// - public Transformation ColorProfile(bool value) - { - return this.Add("cp", value.ToString().ToLower()); - } - - /// - /// - public Transformation DefaultImage(string value) - { - return this.Add("di", value); - } - - /// - /// - public Transformation Dpr(object value) - { - return this.Add("dpr", value); - } - - /// - public Transformation EffectSharpen() - { - return this.Add("e-sharpen", string.Empty); - } - - /// - /// - public Transformation EffectSharpen(int value) - { - return this.Add("e-sharpen", value); - } - - /// - /// - public Transformation EffectUsm(string value) - { - return this.Add("e-usm", value); - } - - /// - public Transformation EffectContrast() - { - return this.Add("e-contrast", string.Empty); - } - - /// - /// - public Transformation EffectContrast(object value) - { - return this.Add("e-contrast", value.ToString().ToLower()); - } - - /// - public Transformation EffectGray() - { - return this.Add("e-grayscale", "true"); - } - - /// - public Transformation EffectShadow() - { - return this.Add("e-shadow", string.Empty); - } - - /// - /// - public Transformation EffectShadow(string value) - { - return this.Add("e-shadow", value); - } - - /// - public Transformation EffectGradient() - { - return this.Add("e-gradient", string.Empty); - } - - /// - /// - public Transformation EffectGradient(string value) - { - return this.Add("e-gradient", value); - } - - /// - public Transformation Original() - { - return this.Add("orig", "true"); - } - - /// - /// Pass an raw transformation string (including chained transformations) - /// - /// A raw transformation string. - public Transformation Raw(string value) - { - return this.Add(value, string.Empty); - } - - private string ConvertCoordinateParam(int paramValue) - { - return paramValue < 0 - ? $"N{Math.Abs(paramValue)}" - : $"{paramValue}"; - } -} diff --git a/Imagekit/Helper/Url.cs b/Imagekit/Helper/Url.cs deleted file mode 100644 index fbf7b154..00000000 --- a/Imagekit/Helper/Url.cs +++ /dev/null @@ -1,184 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Text; -using System.Text.RegularExpressions; -using global::Imagekit.Util; - -[ExcludeFromCodeCoverage] -public class Url -{ - public Dictionary options = new Dictionary(); - - private bool isSrcParameterUsedForUrl; - private Uri parsedUrl; - private Uri parsedHost; - - public Url(Dictionary client) - { - this.options = client; - } - - public string UrlBuilder(string transformationString) - { - Dictionary urlObject = new Dictionary(); - - if (this.options.ContainsKey("path") && this.options.ContainsKey("src")) - { - throw new Exception("Either path or src is required."); - } - else if (this.options.ContainsKey("path")) - { - string path = this.AddLeadingSlash((string)this.options["path"]); - this.parsedUrl = new Uri((string)this.options["urlEndpoint"] + path); - this.parsedHost = new Uri((string)this.options["urlEndpoint"]); - urlObject.Add("protocol", this.parsedHost.Scheme); - urlObject.Add("host", this.options["urlEndpoint"].ToString().Replace(this.parsedHost.Scheme + "://", string.Empty)); - urlObject.Add("pathname", path.Split('?')[0]); - } - else if (this.options.ContainsKey("src")) - { - this.parsedUrl = new Uri((string)this.options["src"]); - this.isSrcParameterUsedForUrl = true; - urlObject.Add("protocol", this.parsedUrl.Scheme); - urlObject.Add("host", this.parsedUrl.Host); - urlObject.Add("pathname", this.parsedUrl.AbsolutePath); - } - else - { - throw new Exception("Either path or src is required."); - } - - // Create correct query parameters - List queryParameters = new List(); - - // Parse query params which are part of the URL - if (this.parsedUrl.Query != null && this.parsedUrl.Query.Length > 1) - { - string[] queryList = this.parsedUrl.Query.Split(new char[] { '&', '?' }); - foreach (var param in queryList) - { - if (string.IsNullOrEmpty(param)) - { - continue; - } - - int index = param.IndexOf('='); - if (index < 0) - { - continue; - } - - queryParameters.Add(this.GetParam(param.Substring(0, index), param.Substring(index + 1))); - } - } - - // parse param passed as queryParameters list - if (this.options.ContainsKey("queryParameters")) - { - foreach (var param in (string[])this.options["queryParameters"]) - { - if (string.IsNullOrEmpty(param)) - { - continue; - } - - int index = param.IndexOf('='); - if (index < 0) - { - throw new Exception(string.Format("Couldn't parse '{0}'!", param)); - } - - queryParameters.Add(this.GetParam(param.Substring(0, index), param.Substring(index + 1))); - } - } - - if (!string.IsNullOrEmpty(transformationString)) - { - if (this.isSrcParameterUsedForUrl || this.AddAsQueryParameter()) - { - queryParameters.Add(this.GetParam(Constants.TransformationParameter, transformationString)); - } - else - { - urlObject["pathname"] = "/tr:" + transformationString + urlObject["pathname"]; - } - } - - urlObject["host"] = this.RemoveTrailingSlash(urlObject["host"]); - - if (queryParameters.Count > 0) - { - urlObject["query"] = string.Join("&", queryParameters); - } - - if (this.options.ContainsKey("signed") && this.options["signed"].Equals(true)) - { - string expiryTimestamp = this.options.ContainsKey("expireSeconds") ? Utils.GetSignatureTimestamp((int)this.options["expireSeconds"]) : Constants.DefaultTimestamp; - if (expiryTimestamp != Constants.DefaultTimestamp) - { - queryParameters.Add(this.GetParam(Constants.TimestampParameter, expiryTimestamp)); - } - - string intermediateUrl = this.GenrateUrl(urlObject); - queryParameters.Add(this.GetParam(Constants.SignatureParameter, this.GetSignature(intermediateUrl, expiryTimestamp))); - - if (queryParameters.Count > 0) - { - urlObject["query"] = string.Join("&", queryParameters); - } - } - - return this.GenrateUrl(urlObject); - } - - public string GenrateUrl(Dictionary urlObject) - { - if (urlObject.ContainsKey("query")) - { - return urlObject["protocol"] + "://" + urlObject["host"] + urlObject["pathname"] + "?" + urlObject["query"]; - } - else - { - return urlObject["protocol"] + "://" + urlObject["host"] + urlObject["pathname"]; - } - } - - public string RemoveTrailingSlash(string str) - { - return str.TrimEnd(new[] { '/' }); - } - - public string AddLeadingSlash(string str) - { - str = str.TrimStart('/'); - str = "/" + str; - return str; - } - - public string GetParam(string key, string param) - { - return $"{Uri.EscapeDataString(key)}={Uri.EscapeDataString(param)}"; - } - - public bool AddAsQueryParameter() - { - if (this.options["transformationPosition"].ToString() == "query") - { - return true; - } - - return false; - } - - public string GetSignature(string url, string expiryTimestamp) - { - var endPoint = this.RemoveTrailingSlash((string)this.options["urlEndpoint"]); - string str = Regex.Replace(url, endPoint + "/", string.Empty) + expiryTimestamp; - return Utils.CalculateSignature(str, Encoding.ASCII.GetBytes((string)this.options["privateKey"])); - } -} diff --git a/Imagekit/Helper/ValidateParamater.cs b/Imagekit/Helper/ValidateParamater.cs deleted file mode 100644 index bbd86776..00000000 --- a/Imagekit/Helper/ValidateParamater.cs +++ /dev/null @@ -1,397 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit.Helper -{ - using System.Collections.Generic; - using global::Imagekit.Constant; - using global::Imagekit.Models; - - public static class ValidateParamater - { - public static string IsValidateUpload(FileCreateRequest obj) - { - string flag = ErrorMessages.InvalidFileValue; - if (obj == null) - { - flag = ErrorMessages.InvalidFileUploadObjValue; - return flag; - } - - if (string.IsNullOrEmpty(obj.fileName)) - { - flag = ErrorMessages.MissingUploadFilenameParameter; - return flag; - } - - if (obj.file != null) - { - flag = string.Empty; - return flag; - } - - return flag; - } - - public static bool IsValidateParam(string input) - { - bool flag = false; - - if (!string.IsNullOrEmpty(input)) - { - flag = true; - return flag; - } - - return flag; - } - - public static bool IsListCheck(List input) - { - bool flag = false; - - if (input.Count > 0) - { - flag = true; - return flag; - } - - return flag; - } - - public static string IsValidateTagRequest(TagsRequest obj) - { - string flag = string.Empty; - if (obj == null) - { - flag = ErrorMessages.InvalidTagValue; - return flag; - } - - if (obj.tags == null) - { - flag = ErrorMessages.InvalidTagParamValue; - return flag; - } - - if (obj.fileIds == null) - { - flag = ErrorMessages.InvalidFiledParamValue; - return flag; - } - - return flag; - } - - public static string IsValidateAiTagRequest(AITagsRequest obj) - { - string flag = string.Empty; - if (obj == null) - { - flag = ErrorMessages.InvalidTagValue; - return flag; - } - - if (obj.AITags == null) - { - flag = ErrorMessages.InvalidTagParamValue; - return flag; - } - - if (obj.fileIds == null) - { - flag = ErrorMessages.InvalidFiledParamValue; - return flag; - } - - return flag; - } - - public static string IsValidateCustomMetaDataFieldCreateRequest(CustomMetaDataFieldCreateRequest obj) - { - string flag = string.Empty; - if (obj == null) - { - flag = ErrorMessages.InvalidMetaTagValue; - return flag; - } - - if (obj.schema == null) - { - flag = ErrorMessages.InvalidMetaTagSchemaValue; - return flag; - } - - if (string.IsNullOrEmpty(obj.name)) - { - flag = ErrorMessages.InvalidMetaTagNameValue; - return flag; - } - - if (string.IsNullOrEmpty(obj.label)) - { - flag = ErrorMessages.InvalidMetaTagLabelValue; - return flag; - } - - return flag; - } - - public static string IsValidateCustomMetaDataFieldUpdateRequest(CustomMetaDataFieldUpdateRequest obj) - { - string flag = string.Empty; - if (obj == null) - { - flag = ErrorMessages.InvalidMetaTagValue; - return flag; - } - - if (obj.schema == null) - { - flag = ErrorMessages.InvalidMetaTagSchemaValue; - return flag; - } - - if (string.IsNullOrEmpty(obj.Id)) - { - flag = ErrorMessages.InvalidMetaTagIdValue; - return flag; - } - - return flag; - } - - public static string IsValidateDeleteFileVersionRequest(DeleteFileVersionRequest obj) - { - string flag = string.Empty; - if (obj == null) - { - flag = ErrorMessages.InvalidDelVerValue; - return flag; - } - - if (string.IsNullOrEmpty(obj.fileId)) - { - flag = ErrorMessages.InvalidFieldIdDelVerValue; - return flag; - } - - if (string.IsNullOrEmpty(obj.versionId)) - { - flag = ErrorMessages.InvalidversionIdDelVerValue; - return flag; - } - - return flag; - } - - public static string IsValidateCopyRequest(CopyFileRequest obj) - { - string flag = string.Empty; - if (obj == null) - { - flag = ErrorMessages.InvalidMoveValue; - return flag; - } - - if (string.IsNullOrEmpty(obj.sourceFilePath)) - { - flag = ErrorMessages.InvalidSourceValue; - return flag; - } - - if (string.IsNullOrEmpty(obj.destinationPath)) - { - flag = ErrorMessages.InvalidDestinationValue; - return flag; - } - - return flag; - } - - public static string IsValidateMoveRequest(MoveFileRequest obj) - { - string flag = string.Empty; - if (obj == null) - { - flag = ErrorMessages.InvalidMoveValue; - return flag; - } - - if (string.IsNullOrEmpty(obj.sourceFilePath)) - { - flag = ErrorMessages.InvalidSourceValue; - return flag; - } - - if (string.IsNullOrEmpty(obj.destinationPath)) - { - flag = ErrorMessages.InvalidDestinationValue; - return flag; - } - - return flag; - } - - public static string IsValidateRenameRequest(RenameFileRequest obj) - { - string flag = string.Empty; - if (obj == null) - { - flag = ErrorMessages.InvalidRenameValue; - return flag; - } - - if (string.IsNullOrEmpty(obj.filePath)) - { - flag = ErrorMessages.InvalidRenameFilePathValue; - return flag; - } - - if (string.IsNullOrEmpty(obj.newFileName)) - { - flag = ErrorMessages.InvalidRenameNewFileNameValue; - return flag; - } - - return flag; - } - - public static string IsValidateParam(string input, string versonid) - { - string flag = string.Empty; - - if (string.IsNullOrEmpty(input) && string.IsNullOrEmpty(versonid)) - { - flag = ErrorMessages.InvalidDelVerValue; - return flag; - } - - if (string.IsNullOrEmpty(input)) - { - flag = ErrorMessages.InvalidFieldIdDelVerValue; - return flag; - } - - if (string.IsNullOrEmpty(versonid)) - { - flag = ErrorMessages.InvalidversionIdDelVerValue; - return flag; - } - - return flag; - } - - public static string IsValidateFolder(CreateFolderRequest createFolderRequest) - { - string flag = string.Empty; - - if (createFolderRequest == null) - { - flag = ErrorMessages.InvalidCreateFolderValue; - return flag; - } - - if (string.IsNullOrEmpty(createFolderRequest.folderName)) - { - flag = ErrorMessages.InvalidfolderNameValue; - return flag; - } - - if (string.IsNullOrEmpty(createFolderRequest.parentFolderPath)) - { - flag = ErrorMessages.InvalidFolderPathValue; - return flag; - } - - return flag; - } - - public static bool IsValidateDeleteFolder(DeleteFolderRequest createFolderRequest) - { - bool flag = false; - - if (createFolderRequest == null) - { - return flag; - } - - if (!string.IsNullOrEmpty(createFolderRequest.folderPath)) - { - flag = true; - return flag; - } - - return flag; - } - - public static string IsValidateCopyFolder(CopyFolderRequest createFolderRequest) - { - string flag = string.Empty; - - if (createFolderRequest == null) - { - flag = ErrorMessages.InvalidCopyFolderValue; - return flag; - } - - if (string.IsNullOrEmpty(createFolderRequest.sourceFolderPath)) - { - flag = ErrorMessages.InvalidCopysourceFolderPathValue; - return flag; - } - - if (string.IsNullOrEmpty(createFolderRequest.destinationPath)) - { - flag = ErrorMessages.InvalidCopydestinationPathValue; - return flag; - } - - return flag; - } - - public static string IsValidateMoveFolder(MoveFolderRequest createFolderRequest) - { - string flag = string.Empty; - - if (createFolderRequest == null) - { - flag = ErrorMessages.InvalidCopyFolderValue; - return flag; - } - - if (string.IsNullOrEmpty(createFolderRequest.sourceFolderPath)) - { - flag = ErrorMessages.InvalidCopysourceFolderPathValue; - return flag; - } - - if (string.IsNullOrEmpty(createFolderRequest.destinationPath)) - { - flag = ErrorMessages.InvalidCopydestinationPathValue; - return flag; - } - - return flag; - } - - public static string IsValidateUpdateFile(FileUpdateRequest obj) - { - string flag = string.Empty; - if (obj == null) - { - flag = ErrorMessages.FileIdMissing; - return flag; - } - - if (string.IsNullOrEmpty(obj.fileId)) - { - flag = ErrorMessages.FileIdMissing; - return flag; - } - - return flag; - } - } -} diff --git a/Imagekit/IImageKit.cs b/Imagekit/IImageKit.cs deleted file mode 100644 index 9ef4ed94..00000000 --- a/Imagekit/IImageKit.cs +++ /dev/null @@ -1,62 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit.Sdk -{ - using System.Collections.Generic; - using global::Imagekit.Models; - - public interface IImagekit - { - ResponseMetaData AddTags(TagsRequest tagsRequest); - - ResponseMetaData BulkDeleteFiles(List fileIds); - - ResponseMetaData CopyFile(CopyFileRequest copyFileRequest); - - ResponseMetaData CopyFolder(CopyFolderRequest copyFolderRequest); - - ResponseMetaData CreateCustomMetaDataFields(CustomMetaDataFieldCreateRequest customMetaDataFieldCreateRequest); - - ResponseMetaData CreateFolder(CreateFolderRequest createFolderRequest); - - ResponseMetaData DeleteCustomMetaDataField(string id); - - ResponseMetaData DeleteFile(string fileId); - - ResponseMetaData DeleteFileVersion(DeleteFileVersionRequest deleteFileVersionRequest); - - ResponseMetaData DeleteFolder(DeleteFolderRequest deleteFolderRequest); - - ResponseMetaData GetBulkJobStatus(string jobId); - - ResponseMetaData GetCustomMetaDataFields(bool includeDeleted); - - ResponseMetaData GetFileDetail(string fileId); - - ResponseMetaData GetFileMetadata(string fileId); - - ResponseMetaData GetFileVersionDetails(string fileId, string versionId); - - ResponseMetaData GetFileVersions(string fileId); - - ResponseMetaData GetRemoteFileMetadata(string url); - - ResponseMetaData MoveFile(MoveFileRequest moveFileRequest); - - ResponseMetaData MoveFolder(MoveFolderRequest moveFolderRequest); - - ResponseMetaData RemoveAITags(AITagsRequest aITagsRequest); - - ResponseMetaData RemoveTags(TagsRequest tagsRequest); - - ResponseMetaData RenameFile(RenameFileRequest renameFileRequest); - - ResponseMetaData RestoreFileVersion(string fileId, string versionId); - - ResponseMetaData UpdateCustomMetaDataFields(CustomMetaDataFieldUpdateRequest customMetaDataFieldUpdateRequest); - - ResponseMetaData Upload(FileCreateRequest fileCreateRequest); - } -} \ No newline at end of file diff --git a/Imagekit/Imagekit.cs b/Imagekit/Imagekit.cs deleted file mode 100644 index e877c1f6..00000000 --- a/Imagekit/Imagekit.cs +++ /dev/null @@ -1,367 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit.Sdk -{ - using System; - using System.Collections.Generic; - using System.Text; - using System.Threading.Tasks; - using global::Imagekit.Models; - using global::Imagekit.Models.Response; - using global::Imagekit.Util; - - public class ImagekitClient : BaseImagekit - { - private readonly RestClient restClient; - private readonly string privateKey; - - public ImagekitClient(string publicKey, string privateKey, string urlEndPoint) - : base(privateKey, urlEndPoint, "path") - { - this.restClient = new RestClient(privateKey, urlEndPoint, new System.Net.Http.HttpClient()); - this.Add("privateKey", privateKey); - this.Add("publicKey", publicKey); - this.privateKey = privateKey; - } - - public Result Upload(FileCreateRequest fileCreateRequest) - { - return (Result)this.restClient.Upload(fileCreateRequest); - } - - public Result UpdateFileDetail(FileUpdateRequest fileUpdateRequest) - { - return (Result)this.restClient.UpdateFileDetail(fileUpdateRequest); - } - - public async Task UpdateFileDetailAsync(FileUpdateRequest fileUpdateRequest) - { - return (Result)await this.restClient.UpdateFileDetailAsync(fileUpdateRequest); - } - - public ResultCache PurgeCache(string url) - { - return (ResultCache)this.restClient.PurgeCache(url); - } - - public async Task PurgeCacheAsync(string url) - { - return (ResultCache)await this.restClient.PurgeCacheAsync(url); - } - - public ResultCacheStatus PurgeStatus(string purgeRequestId) - { - return (ResultCacheStatus)this.restClient.PurgeStatus(purgeRequestId); - } - - public async Task PurgeStatusAsync(string url) - { - return (ResultCacheStatus)await this.restClient.PurgeStatusAsync(url); - } - - public Result GetFileDetail(string url) - { - return (Result)this.restClient.GetFileDetail(url); - } - - public ResultList GetFileListRequest(GetFileListRequest getFileListRequest) - { - return (ResultList)this.restClient.GetFileListRequest(getFileListRequest); - } - - public ResultMetaData GetFileMetadata(string fileId) - { - return (ResultMetaData)this.restClient.GetFileMetaData(fileId); - } - - public ResultMetaData GetRemoteFileMetadata(string url) - { - return (ResultMetaData)this.restClient.GetRemoteFileMetaData(url); - } - - public ResultDelete DeleteFile(string fileId) - { - return (ResultDelete)this.restClient.DeleteFile(fileId); - } - - public ResultFileDelete BulkDeleteFiles(List fileIds) - { - return (ResultFileDelete)this.restClient.BulkDeleteFiles(fileIds); - } - - public ResultTags AddTags(TagsRequest tagsRequest) - { - return (ResultTags)this.restClient.ManageTags(tagsRequest, "addTags"); - } - - public void Clear() - { - throw new NotImplementedException(); - } - - public ResultTags RemoveAITags(AITagsRequest aITagsRequest) - { - return (ResultTags)this.restClient.RemoveAITags(aITagsRequest); - } - - public ResultTags RemoveTags(TagsRequest tagsRequest) - { - return (ResultTags)this.restClient.ManageTags(tagsRequest, "removeTags"); - } - - public ResultCustomMetaDataFieldList GetCustomMetaDataFields(bool includeDeleted) - { - return (ResultCustomMetaDataFieldList)this.restClient.GetCustomMetaDataFields(includeDeleted); - } - - public ResultCustomMetaDataField CreateCustomMetaDataFields( - CustomMetaDataFieldCreateRequest customMetaDataFieldCreateRequest) - { - return (ResultCustomMetaDataField)this.restClient.CreateCustomMetaDataFields(customMetaDataFieldCreateRequest); - } - - public ResultNoContent DeleteCustomMetaDataField(string id) - { - return (ResultNoContent)this.restClient.DeleteCustomMetaDataField(id); - } - - public ResultCustomMetaDataField UpdateCustomMetaDataFields( - CustomMetaDataFieldUpdateRequest customMetaDataFieldUpdateRequest) - { - return (ResultCustomMetaDataField)this.restClient.UpdateCustomMetaDataFields(customMetaDataFieldUpdateRequest); - } - - public ResultNoContent DeleteFileVersion(DeleteFileVersionRequest deleteFileVersionRequest) - { - return (ResultNoContent)this.restClient.DeleteFileVersion(deleteFileVersionRequest); - } - - public ResultNoContent CopyFile(CopyFileRequest copyFileRequest) - { - return (ResultNoContent)this.restClient.CopyFile(copyFileRequest); - } - - public ResultNoContent MoveFile(MoveFileRequest moveFileRequest) - { - return (ResultNoContent)this.restClient.MoveFile(moveFileRequest); - } - - public ResultRenameFile RenameFile(RenameFileRequest renameFileRequest) - { - return (ResultRenameFile)this.restClient.RenameFile(renameFileRequest); - } - - public Result RestoreFileVersion(string fileId, string versionId) - { - return (Result)this.restClient.RestoreFileVersion(fileId, versionId); - } - - public ResultEmptyBlock CreateFolder(CreateFolderRequest createFolderRequest) - { - return (ResultEmptyBlock)this.restClient.CreateFolder(createFolderRequest); - } - - public ResultNoContent DeleteFolder(DeleteFolderRequest deleteFolderRequest) - { - return (ResultNoContent)this.restClient.DeleteFolder(deleteFolderRequest); - } - - public ResultOfFolderActions CopyFolder(CopyFolderRequest copyFolderRequest) - { - return (ResultOfFolderActions)this.restClient.CopyFolder(copyFolderRequest); - } - - public ResultOfFolderActions MoveFolder(MoveFolderRequest moveFolderRequest) - { - return (ResultOfFolderActions)this.restClient.MoveFolder(moveFolderRequest); - } - - public ResultBulkJobStatus GetBulkJobStatus(string jobId) - { - return (ResultBulkJobStatus)this.restClient.GetBulkJobStatus(jobId); - } - - public ResultFileVersions GetFileVersions(string fileId) - { - return (ResultFileVersions)this.restClient.GetFileVersions(fileId); - } - - public ResultFileVersionDetails GetFileVersionDetails(string fileId, string versionId) - { - return (ResultFileVersionDetails)this.restClient.GetFileVersionDetails(fileId, versionId); - } - - public async Task UploadAsync(FileCreateRequest fileCreateRequest) - { - return (Result)await this.restClient.UploadAsync(fileCreateRequest); - } - - public async Task GetFileDetailAsync(string fileId) - { - return await this.restClient.GetFileDetailAsync(fileId); - } - - public async Task GetFileMetadataAsync(string fileId) - { - return (Result)await this.restClient.GetFileMetaDataAsync(fileId); - } - - public async Task GetRemoteFileMetadataAsync(string url) - { - return (ResultMetaData)await this.restClient.GetRemoteFileMetaDataAsync(url); - } - - public async Task DeleteFileAsync(string fileId) - { - return (ResultDelete)await this.restClient.DeleteFileAsync(fileId); - } - - public async Task BulkDeleteFilesAsync(List fileIds) - { - return (ResultFileDelete)await this.restClient.BulkDeleteFilesAsync(fileIds); - } - - public async Task AddTagsAsync(TagsRequest tagsRequest) - { - return (ResultTags)await this.restClient.ManageTagsAsync(tagsRequest, "addTags"); - } - - public async Task RemoveAITagsAsync(AITagsRequest aITagsRequest) - { - return (ResultTags)await this.restClient.RemoveAITagsAsync(aITagsRequest); - } - - public async Task RemoveTagsAsync(TagsRequest tagsRequest) - { - return (ResultTags)await this.restClient.ManageTagsAsync(tagsRequest, "removeTags"); - } - - public async Task GetCustomMetaDataFieldsAsync(bool includeDeleted) - { - return (ResultCustomMetaDataFieldList)await this.restClient.GetCustomMetaDataFieldsAsync(includeDeleted); - } - - public async Task CreateCustomMetaDataFieldsAsync( - CustomMetaDataFieldCreateRequest customMetaDataFieldCreateRequest) - { - return (ResultCustomMetaDataField)await this.restClient.CreateCustomMetaDataFieldsAsync(customMetaDataFieldCreateRequest); - } - - public async Task DeleteCustomMetaDataFieldAsync(string id) - { - return (ResultNoContent)await this.restClient.DeleteCustomMetaDataFieldAsync(id); - } - - public async Task UpdateCustomMetaDataFieldsAsync( - CustomMetaDataFieldUpdateRequest customMetaDataFieldUpdateRequest) - { - return (ResultCustomMetaDataField)await this.restClient.UpdateCustomMetaDataFieldsAsync(customMetaDataFieldUpdateRequest); - } - - public async Task DeleteFileVersionAsync(DeleteFileVersionRequest deleteFileVersionRequest) - { - return (ResultNoContent)await this.restClient.DeleteFileVersionAsync(deleteFileVersionRequest); - } - - public async Task CopyFileAsync(CopyFileRequest copyFileRequest) - { - return (ResultNoContent)await this.restClient.CopyFileAsync(copyFileRequest); - } - - public async Task MoveFileAsync(MoveFileRequest moveFileRequest) - { - return (ResultNoContent)await this.restClient.MoveFileAsync(moveFileRequest); - } - - public async Task RenameFileAsync(RenameFileRequest renameFileRequest) - { - return await this.restClient.RenameFileAsync(renameFileRequest); - } - - public async Task RestoreFileVersionAsync(string fileId, string versionId) - { - return (ResultRenameFile)await this.restClient.RestoreFileVersionAsync(fileId, versionId); - } - - public async Task CreateFolderAsync(CreateFolderRequest createFolderRequest) - { - return (ResultEmptyBlock)await this.restClient.CreateFolderAsync(createFolderRequest); - } - - public async Task DeleteFolderAsync(DeleteFolderRequest deleteFolderRequest) - { - return (ResultNoContent)await this.restClient.DeleteFolderAsync(deleteFolderRequest); - } - - public async Task CopyFolderAsync(CopyFolderRequest copyFolderRequest) - { - return (ResultOfFolderActions)await this.restClient.CopyFolderAsync(copyFolderRequest); - } - - public async Task MoveFolderAsync(MoveFolderRequest moveFolderRequest) - { - return await this.restClient.MoveFolderAsync(moveFolderRequest); - } - - public async Task GetBulkJobStatusAsync(string jobId) - { - return (ResultOfFolderActions)await this.restClient.GetBulkJobStatusAsync(jobId); - } - - public async Task GetFileVersionsAsync(string fileId) - { - return (ResultFileVersions)await this.restClient.GetFileVersionsAsync(fileId); - } - - public async Task GetFileVersionDetailsAsync(string fileId, string versionId) - { - return (ResultFileVersionDetails)await this.restClient.GetFileVersionDetailsAsync(fileId, versionId); - } - - public async Task GetFileListRequestAsync(GetFileListRequest getFileListRequest) - { - return (ResultList)await this.restClient.GetFileListRequestAsync(getFileListRequest); - } - - public int PHashDistance(string firstHex, string secondHex) - { - return Calculation.GetHammingDistance(firstHex, secondHex); - } - - /// - /// Generate Auth params for client-side upload - /// - /// Random Token String - /// Expire time for the token - /// Returns Authparams including token, expiry time and signature. - public AuthParamResponse GetAuthenticationParameters(string token = null, string expire = null) - { - var dEFAULT_TIME_DIFF = 60 * 30; - - AuthParamResponse authParameters = new AuthParamResponse(); - authParameters.token = token; - authParameters.expire = expire; - authParameters.signature = string.Empty; - string defaultExpire = Utils.GetSignatureTimestamp(dEFAULT_TIME_DIFF); - - if (string.IsNullOrEmpty(expire)) - { - expire = defaultExpire; - } - - if (string.IsNullOrEmpty(token)) - { - token = Guid.NewGuid().ToString(); - } - - string signature = Utils.CalculateSignature(token + expire, Encoding.ASCII.GetBytes((string)this.privateKey)); - authParameters.token = token; - authParameters.expire = expire; - authParameters.signature = signature; - - return authParameters; - } - } -} \ No newline at end of file diff --git a/Imagekit/Imagekit.csproj b/Imagekit/Imagekit.csproj deleted file mode 100644 index 013cc8ba..00000000 --- a/Imagekit/Imagekit.csproj +++ /dev/null @@ -1,124 +0,0 @@ - - - - Library - Imagekit - Imagekit - net7.0;net5.0;net6.0;netcoreapp3.1;netstandard2.0 - 8 - © Imagekit Private Limited 2019 - false - - https://github.com/imagekit-developer/imagekit-dotnet/blob/v2.x/LICENSE - Imagekit - https://imagekit.io - Image Resize Crop SmartCrop Rotate Quality Watermark Gif Jpg Jpeg Bitmap PNG Tiff Webp Webm Svg Optimization - Imagekit.io DotNet Library - Library to Integrate Imagekit.io Service. - Imagekit - true - 5.0.0 - true - false - 5.0.0 - git@github.com:imagekit-developer/imagekit-dotnet.git - true - -Fixes - false - - - - - 1701;1702;1591;NU5125;NU5048;NU1504;NU5125;SYSLIB0014; - $(WarningsAsErrors); - - - - 1701;1702;1591;NU5125;NU5048;NU1504;NU5125;SYSLIB0014; - $(WarningsAsErrors); - - - - 1701;1702;1591;NU5125;NU5048;NU1504;NU5125;SYSLIB0014; - $(WarningsAsErrors); - 4 - - - - 1701;1702;1591;NU5125;NU5048;NU1504;NU5125;SYSLIB0014; - $(WarningsAsErrors); - 4 - - - - 1701;1702;1591;NU5125;NU5048;NU1504;NU5125;SYSLIB0014; - $(WarningsAsErrors); - 4 - - - - 1701;1702;1591;NU5125;NU5048;NU1504;NU5125;SYSLIB0014; - $(WarningsAsErrors); - 4 - - - - 1701;1702;1591;NU5125;NU5048;NU1504;NU5125;SYSLIB0014; - $(WarningsAsErrors); - - - - 1701;1702;1591;NU5125;NU5048;NU1504;NU5125;SYSLIB0014; - $(WarningsAsErrors); - - - - 1701;1702;1591;NU5125;NU5048;NU1504;NU5125;SYSLIB0014; - $(WarningsAsErrors); - - - - 1701;1702;1591;NU5125;NU5048;NU1504;NU5125;SYSLIB0014; - $(WarningsAsErrors); - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Imagekit/Models/AITagsRequest.cs b/Imagekit/Models/AITagsRequest.cs deleted file mode 100644 index 4d1db13d..00000000 --- a/Imagekit/Models/AITagsRequest.cs +++ /dev/null @@ -1,23 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit -{ - using System.Collections.Generic; - - public class AITagsRequest - { - public List fileIds - { - get; - set; - } - - public List AITags - { - get; - set; - } - } -} \ No newline at end of file diff --git a/Imagekit/Models/AuthParamResponse.cs b/Imagekit/Models/AuthParamResponse.cs deleted file mode 100644 index 67e8f446..00000000 --- a/Imagekit/Models/AuthParamResponse.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Imagekit.Models -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; - using Newtonsoft.Json; - - public class AuthParamResponse - { - public string token { get; set; } - - public string expire { get; set; } - - public string signature { get; set; } - } -} diff --git a/Imagekit/Models/CopyFileRequest.cs b/Imagekit/Models/CopyFileRequest.cs deleted file mode 100644 index 6dfb8bcf..00000000 --- a/Imagekit/Models/CopyFileRequest.cs +++ /dev/null @@ -1,15 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit.Models -{ - public class CopyFileRequest - { - public string sourceFilePath { get; set; } - - public string destinationPath { get; set; } - - public bool includeFileVersions { get; set; } - } -} \ No newline at end of file diff --git a/Imagekit/Models/CopyFolderRequest.cs b/Imagekit/Models/CopyFolderRequest.cs deleted file mode 100644 index 1e4b452c..00000000 --- a/Imagekit/Models/CopyFolderRequest.cs +++ /dev/null @@ -1,15 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit.Models -{ - public class CopyFolderRequest - { - public string sourceFolderPath { get; set; } - - public string destinationPath { get; set; } - - public bool includeFileVersions { get; set; } - } -} \ No newline at end of file diff --git a/Imagekit/Models/CreateFolderRequest.cs b/Imagekit/Models/CreateFolderRequest.cs deleted file mode 100644 index b308c51f..00000000 --- a/Imagekit/Models/CreateFolderRequest.cs +++ /dev/null @@ -1,13 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit.Models -{ - public class CreateFolderRequest - { - public string folderName { get; set; } - - public string parentFolderPath { get; set; } - } -} \ No newline at end of file diff --git a/Imagekit/Models/CustomMetaDataFieldCreateRequest.cs b/Imagekit/Models/CustomMetaDataFieldCreateRequest.cs deleted file mode 100644 index 7286fe48..00000000 --- a/Imagekit/Models/CustomMetaDataFieldCreateRequest.cs +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit.Models -{ - public class CustomMetaDataFieldCreateRequest - { - public string name { get; set; } - - public string label { get; set; } - - public CustomMetaDataFieldSchemaObject schema { get; set; } - } - - public class CustomMetaDataFieldUploadRequest - { - public string name { get; set; } - - public CustomMetaDataFieldSchemaObject schema { get; set; } - } -} \ No newline at end of file diff --git a/Imagekit/Models/CustomMetaDataFieldSchemaObject.cs b/Imagekit/Models/CustomMetaDataFieldSchemaObject.cs deleted file mode 100644 index 385a7a02..00000000 --- a/Imagekit/Models/CustomMetaDataFieldSchemaObject.cs +++ /dev/null @@ -1,51 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit.Models -{ - using System.ComponentModel; - - public class CustomMetaDataFieldSchemaObject - { - public string type { get; set; } - - public string[] selectOptions { get; set; } - - public object defaultValue { get; set; } - - private bool IsValueRequired; - - [DefaultValue(false)] - public bool isValueRequired - { - get - { - return this.IsValueRequired; - } - - set - { - this.IsValueRequired = value; - } - } - - public object minValue { get; set; } - - public object maxValue { get; set; } - - public int minLength { get; set; } - - public int maxLength { get; set; } - - public enum CustomMetaDataTypeEnum - { - Text, - Textarea, - Number, - Date, - SingleSelect, - MultiSelect, - } - } -} \ No newline at end of file diff --git a/Imagekit/Models/CustomMetaDataFieldUpdateRequest.cs b/Imagekit/Models/CustomMetaDataFieldUpdateRequest.cs deleted file mode 100644 index 9d920e1b..00000000 --- a/Imagekit/Models/CustomMetaDataFieldUpdateRequest.cs +++ /dev/null @@ -1,13 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit.Models -{ - public class CustomMetaDataFieldUpdateRequest - { - public string Id { get; set; } - - public CustomMetaDataFieldSchemaObject schema { get; set; } - } -} \ No newline at end of file diff --git a/Imagekit/Models/DeleteFileVersionRequest.cs b/Imagekit/Models/DeleteFileVersionRequest.cs deleted file mode 100644 index c0aa7d78..00000000 --- a/Imagekit/Models/DeleteFileVersionRequest.cs +++ /dev/null @@ -1,13 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit.Models -{ - public class DeleteFileVersionRequest - { - public string fileId { get; set; } - - public string versionId { get; set; } - } -} \ No newline at end of file diff --git a/Imagekit/Models/DeleteFolderRequest.cs b/Imagekit/Models/DeleteFolderRequest.cs deleted file mode 100644 index dcfe9cff..00000000 --- a/Imagekit/Models/DeleteFolderRequest.cs +++ /dev/null @@ -1,11 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit.Models -{ - public class DeleteFolderRequest - { - public string folderPath { get; set; } - } -} \ No newline at end of file diff --git a/Imagekit/Models/ExtensionModel.cs b/Imagekit/Models/ExtensionModel.cs deleted file mode 100644 index 32b5f270..00000000 --- a/Imagekit/Models/ExtensionModel.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace Imagekit.Models -{ - using System.Collections.Generic; - - public class Extension - { - public string name { get; set; } - } - - public class options - { - public bool add_shadow { get; set; } - - public bool? semitransparency { get; set; } - - public string? bg_color { get; set; } - - public string? bg_image_url { get; set; } - } - - public class BackGroundImage : Extension - { - public options options { get; set; } - } - - public class AutoTags : Extension - { - public int? minConfidence { get; set; } - - public int? maxTags { get; set; } - } -} diff --git a/Imagekit/Models/FileCreateRequest.cs b/Imagekit/Models/FileCreateRequest.cs deleted file mode 100644 index 9787a6bc..00000000 --- a/Imagekit/Models/FileCreateRequest.cs +++ /dev/null @@ -1,47 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -using System; -using System.Collections; -using System.Collections.Generic; -using Imagekit.Models; - -public class FileCreateRequest -{ - public string fileName { get; set; } - - public bool useUniqueFileName { get; set; } - - public List tags { get; set; } - - public string folder { get; set; } - - public string customCoordinates { get; set; } - - public List responseFields { get; set; } - - public List extensions { get; set; } - - public string webhookUrl { get; set; } - - public bool overwriteFile { get; set; } - - public bool overwriteAITags { get; set; } - - public bool overwriteTags { get; set; } - - public bool overwriteCustomMetadata { get; set; } - - public Hashtable customMetadata { get; set; } - - public object file { get; set; } - - public bool isPrivateFile { get; set; } - - public UploadTransformation transformation { get; set; } - - public string checks { get; set; } - - public bool? isPublished { get; set; } -} \ No newline at end of file diff --git a/Imagekit/Models/FileUpdateRequest.cs b/Imagekit/Models/FileUpdateRequest.cs deleted file mode 100644 index 9d1bd552..00000000 --- a/Imagekit/Models/FileUpdateRequest.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Imagekit.Models -{ - using System.Collections; - using System.Collections.Generic; - - public class FileUpdateRequest - { - public string fileId { get; set; } - - public List removeAITags { get; set; } - - public string webhookUrl { get; set; } - - public List extensions { get; set; } - - public List tags { get; set; } - - public string customCoordinates { get; set; } - - public Hashtable customMetadata { get; set; } - - public PublishStatus publish { get; set; } - } -} diff --git a/Imagekit/Models/GetFileListRequest.cs b/Imagekit/Models/GetFileListRequest.cs deleted file mode 100644 index 67da7164..00000000 --- a/Imagekit/Models/GetFileListRequest.cs +++ /dev/null @@ -1,27 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit.Models -{ - public class GetFileListRequest - { - public string Name { get; set; } - - public string Type { get; set; } - - public string Sort { get; set; } - - public string Path { get; set; } - - public string SearchQuery { get; set; } - - public string FileType { get; set; } - - public int Limit { get; set; } - - public int Skip { get; set; } - - public string[] Tags { get; set; } - } -} diff --git a/Imagekit/Models/MoveFileRequest.cs b/Imagekit/Models/MoveFileRequest.cs deleted file mode 100644 index 0b247186..00000000 --- a/Imagekit/Models/MoveFileRequest.cs +++ /dev/null @@ -1,10 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -public class MoveFileRequest -{ - public string sourceFilePath { get; set; } - - public string destinationPath { get; set; } -} \ No newline at end of file diff --git a/Imagekit/Models/MoveFolderRequest.cs b/Imagekit/Models/MoveFolderRequest.cs deleted file mode 100644 index 8cc3818c..00000000 --- a/Imagekit/Models/MoveFolderRequest.cs +++ /dev/null @@ -1,10 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -public class MoveFolderRequest -{ - public string sourceFolderPath { get; set; } - - public string destinationPath { get; set; } -} \ No newline at end of file diff --git a/Imagekit/Models/PublishStatus.cs b/Imagekit/Models/PublishStatus.cs deleted file mode 100644 index be11998e..00000000 --- a/Imagekit/Models/PublishStatus.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Imagekit.Models -{ - using System.Collections.Generic; - - public class PublishStatus - { - public bool isPublished { get; set; } - public bool? includeFileVersions { get; set; } - } - -} diff --git a/Imagekit/Models/RenameFileRequest.cs b/Imagekit/Models/RenameFileRequest.cs deleted file mode 100644 index 6585492c..00000000 --- a/Imagekit/Models/RenameFileRequest.cs +++ /dev/null @@ -1,28 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -using System.ComponentModel; - -public class RenameFileRequest -{ - public string filePath { get; set; } - - public string newFileName { get; set; } - - private bool IspurgeCache; - - [DefaultValue(false)] - public bool purgeCache - { - get - { - return this.IspurgeCache; - } - - set - { - this.IspurgeCache = value; - } - } -} \ No newline at end of file diff --git a/Imagekit/Models/Response/MetaData.cs b/Imagekit/Models/Response/MetaData.cs deleted file mode 100644 index 2cf6288d..00000000 --- a/Imagekit/Models/Response/MetaData.cs +++ /dev/null @@ -1,30 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit.Models.Response -{ - using System.Diagnostics.CodeAnalysis; - - [ExcludeFromCodeCoverage] - public class MetaData - { - public float Height { get; set; } - - public float Width { get; set; } - - public float Size { get; set; } - - public string Format { get; set; } - - public bool HasColorProfile { get; set; } - - public float Quality { get; set; } - - public float Density { get; set; } - - public bool HasTransparency { get; set; } - - public string PHash { get; set; } - } -} diff --git a/Imagekit/Models/Response/Result.cs b/Imagekit/Models/Response/Result.cs deleted file mode 100644 index 42d86dac..00000000 --- a/Imagekit/Models/Response/Result.cs +++ /dev/null @@ -1,109 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using Newtonsoft.Json; - -[ExcludeFromCodeCoverage] - -// Root myDeserializedClass = JsonConvert.DeserializeObject(myJsonResponse); -public class Exif -{ -} - -public class ExtensionStatus -{ - [JsonProperty("remove-bg")] - public string RemoveBg { get; set; } - - [JsonProperty("google-auto-tagging")] - public string GoogleAutoTagging { get; set; } -} - -public class Metadata -{ - public int height { get; set; } - - public int width { get; set; } - - public int size { get; set; } - - public string format { get; set; } - - public bool hasColorProfile { get; set; } - - public int quality { get; set; } - - public int density { get; set; } - - public bool hasTransparency { get; set; } - - public Exif exif { get; set; } - - public string pHash { get; set; } -} - -public class Result : ResponseMetaData -{ - public string fileId { get; set; } - - public string name { get; set; } - - public int size { get; set; } - - public VersionInfo versionInfo { get; set; } - - public string filePath { get; set; } - - public string url { get; set; } - - public string fileType { get; set; } - - public int height { get; set; } - - public int width { get; set; } - - public string thumbnailUrl { get; set; } - - public List tags { get; set; } - - public object AITags { get; set; } - - public bool isPrivateFile { get; set; } - - public string customCoordinates { get; set; } - - public Metadata metadata { get; set; } - - public ExtensionStatus extensionStatus { get; set; } -} - -public class VersionInfo -{ - public string id { get; set; } - - public string name { get; set; } -} - -public class CustomMetadata -{ -} - -public class EmbeddedMetadata -{ - public int XResolution { get; set; } - - public int YResolution { get; set; } - - public DateTime DateCreated { get; set; } - - public DateTime DateTimeCreated { get; set; } -} - -public class ResultDelete : ResponseMetaData -{ - public string fileId { get; set; } -} \ No newline at end of file diff --git a/Imagekit/Models/Response/ResultBulkJobStatus.cs b/Imagekit/Models/Response/ResultBulkJobStatus.cs deleted file mode 100644 index f2b34ca2..00000000 --- a/Imagekit/Models/Response/ResultBulkJobStatus.cs +++ /dev/null @@ -1,14 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// -using System.Diagnostics.CodeAnalysis; - -[ExcludeFromCodeCoverage] -public class ResultBulkJobStatus : ResponseMetaData -{ - public string JobId { get; set; } - - public string Type { get; set; } - - public string Status { get; set; } -} \ No newline at end of file diff --git a/Imagekit/Models/Response/ResultCache.cs b/Imagekit/Models/Response/ResultCache.cs deleted file mode 100644 index 23ce9589..00000000 --- a/Imagekit/Models/Response/ResultCache.cs +++ /dev/null @@ -1,13 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -using System.Diagnostics.CodeAnalysis; - -[ExcludeFromCodeCoverage] -public class ResultCache : ResponseMetaData -{ - public string Help { get; set; } - - public string RequestId { get; set; } -} \ No newline at end of file diff --git a/Imagekit/Models/Response/ResultCacheStatus.cs b/Imagekit/Models/Response/ResultCacheStatus.cs deleted file mode 100644 index db95f5b9..00000000 --- a/Imagekit/Models/Response/ResultCacheStatus.cs +++ /dev/null @@ -1,13 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -using System.Diagnostics.CodeAnalysis; - -[ExcludeFromCodeCoverage] -public class ResultCacheStatus : ResponseMetaData -{ - public string Help { get; set; } - - public string Status { get; set; } -} \ No newline at end of file diff --git a/Imagekit/Models/Response/ResultCustomMetaDataField.cs b/Imagekit/Models/Response/ResultCustomMetaDataField.cs deleted file mode 100644 index 12b807b5..00000000 --- a/Imagekit/Models/Response/ResultCustomMetaDataField.cs +++ /dev/null @@ -1,18 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -using System.Diagnostics.CodeAnalysis; -using Imagekit.Models; - -[ExcludeFromCodeCoverage] -public class ResultCustomMetaDataField : ResponseMetaData -{ - public string id { get; set; } - - public string name { get; set; } - - public string label { get; set; } - - public CustomMetaDataFieldSchemaObject Schema { get; set; } -} \ No newline at end of file diff --git a/Imagekit/Models/Response/ResultCustomMetaDataFieldList.cs b/Imagekit/Models/Response/ResultCustomMetaDataFieldList.cs deleted file mode 100644 index 6ad9b067..00000000 --- a/Imagekit/Models/Response/ResultCustomMetaDataFieldList.cs +++ /dev/null @@ -1,12 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -[ExcludeFromCodeCoverage] -public class ResultCustomMetaDataFieldList : ResponseMetaData -{ - public List ResultCustomMetaDataFieldList1 { get; set; } = new List(); -} \ No newline at end of file diff --git a/Imagekit/Models/Response/ResultEmptyBlock.cs b/Imagekit/Models/Response/ResultEmptyBlock.cs deleted file mode 100644 index c3e53d5f..00000000 --- a/Imagekit/Models/Response/ResultEmptyBlock.cs +++ /dev/null @@ -1,9 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// -using System.Diagnostics.CodeAnalysis; - -[ExcludeFromCodeCoverage] -public class ResultEmptyBlock : ResponseMetaData -{ -} \ No newline at end of file diff --git a/Imagekit/Models/Response/ResultException.cs b/Imagekit/Models/Response/ResultException.cs deleted file mode 100644 index cd84c4d2..00000000 --- a/Imagekit/Models/Response/ResultException.cs +++ /dev/null @@ -1,12 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// -using System.Diagnostics.CodeAnalysis; - -[ExcludeFromCodeCoverage] -public class ResultException : ResponseMetaData -{ - private string Message { get; set; } - - private string Help { get; set; } -} \ No newline at end of file diff --git a/Imagekit/Models/Response/ResultFileDelete.cs b/Imagekit/Models/Response/ResultFileDelete.cs deleted file mode 100644 index 06da1f29..00000000 --- a/Imagekit/Models/Response/ResultFileDelete.cs +++ /dev/null @@ -1,16 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -[ExcludeFromCodeCoverage] -public class ResultFileDelete : ResponseMetaData -{ - public string Help { get; set; } - - public List SuccessfullyDeletedfileIds { get; set; } - - public List MissingfileIds { get; set; } -} \ No newline at end of file diff --git a/Imagekit/Models/Response/ResultFileVersionDetails.cs b/Imagekit/Models/Response/ResultFileVersionDetails.cs deleted file mode 100644 index 4e2108fc..00000000 --- a/Imagekit/Models/Response/ResultFileVersionDetails.cs +++ /dev/null @@ -1,54 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using Newtonsoft.Json.Linq; - -[ExcludeFromCodeCoverage] -public class ResultFileVersionDetails : ResponseMetaData -{ - public string type { get; set; } - - public string name { get; set; } - - public DateTime createdAt { get; set; } - - public DateTime updatedAt { get; set; } - - public string fileId { get; set; } - - public object tags { get; set; } - - public object AITags { get; set; } - - public VersionInfo versionInfo { get; set; } - - public EmbeddedMetadata embeddedMetadata { get; set; } - - public object customCoordinates { get; set; } - - public CustomMetadata customMetadata { get; set; } - - public bool isPrivateFile { get; set; } - - public string url { get; set; } - - public string thumbnail { get; set; } - - public string fileType { get; set; } - - public string filePath { get; set; } - - public int height { get; set; } - - public int width { get; set; } - - public int size { get; set; } - - public bool hasAlpha { get; set; } - - public string mime { get; set; } -} \ No newline at end of file diff --git a/Imagekit/Models/Response/ResultFileVersions.cs b/Imagekit/Models/Response/ResultFileVersions.cs deleted file mode 100644 index 14120fd3..00000000 --- a/Imagekit/Models/Response/ResultFileVersions.cs +++ /dev/null @@ -1,12 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -[ExcludeFromCodeCoverage] -public class ResultFileVersions : ResponseMetaData -{ - public List ResultFileVersionDetailsList { get; set; } = new List(); -} \ No newline at end of file diff --git a/Imagekit/Models/Response/ResultList.cs b/Imagekit/Models/Response/ResultList.cs deleted file mode 100644 index 34aeb73c..00000000 --- a/Imagekit/Models/Response/ResultList.cs +++ /dev/null @@ -1,60 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -[ExcludeFromCodeCoverage] -public class ResultList : ResponseMetaData -{ - public string Help { get; set; } - - public List FileList { get; set; } -} - -public class Root -{ - public string type { get; set; } - - public string name { get; set; } - - public DateTime createdAt { get; set; } - - public DateTime updatedAt { get; set; } - - public string fileId { get; set; } - - public List tags { get; set; } - - public object AITags { get; set; } - - public VersionInfo versionInfo { get; set; } - - public EmbeddedMetadata embeddedMetadata { get; set; } - - public string customCoordinates { get; set; } - - public CustomMetadata customMetadata { get; set; } - - public bool isPrivateFile { get; set; } - - public string url { get; set; } - - public string thumbnail { get; set; } - - public string fileType { get; set; } - - public string filePath { get; set; } - - public int height { get; set; } - - public int width { get; set; } - - public int size { get; set; } - - public bool hasAlpha { get; set; } - - public string mime { get; set; } -} diff --git a/Imagekit/Models/Response/ResultMetaData.cs b/Imagekit/Models/Response/ResultMetaData.cs deleted file mode 100644 index a2310f72..00000000 --- a/Imagekit/Models/Response/ResultMetaData.cs +++ /dev/null @@ -1,16 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit.Models.Response -{ - using System.Diagnostics.CodeAnalysis; - - [ExcludeFromCodeCoverage] - public class ResultMetaData : ResponseMetaData - { - public string Help { get; set; } - - public MetaData Results { get; set; } - } -} diff --git a/Imagekit/Models/Response/ResultNoContent.cs b/Imagekit/Models/Response/ResultNoContent.cs deleted file mode 100644 index 7c5b2a4e..00000000 --- a/Imagekit/Models/Response/ResultNoContent.cs +++ /dev/null @@ -1,10 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// -using System.Diagnostics.CodeAnalysis; - -[ExcludeFromCodeCoverage] -public class ResultNoContent : ResponseMetaData -{ - private ResponseMetaData ResponseMetaData { get; set; } = new ResponseMetaData(); -} \ No newline at end of file diff --git a/Imagekit/Models/Response/ResultOfFolderActions.cs b/Imagekit/Models/Response/ResultOfFolderActions.cs deleted file mode 100644 index be320ce0..00000000 --- a/Imagekit/Models/Response/ResultOfFolderActions.cs +++ /dev/null @@ -1,11 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -using System.Diagnostics.CodeAnalysis; - -[ExcludeFromCodeCoverage] -public class ResultOfFolderActions : ResponseMetaData -{ - public string JobId { get; set; } -} \ No newline at end of file diff --git a/Imagekit/Models/Response/ResultRenameFile.cs b/Imagekit/Models/Response/ResultRenameFile.cs deleted file mode 100644 index 4fc88224..00000000 --- a/Imagekit/Models/Response/ResultRenameFile.cs +++ /dev/null @@ -1,10 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// -using System.Diagnostics.CodeAnalysis; - -[ExcludeFromCodeCoverage] -public class ResultRenameFile : ResponseMetaData -{ - public string PurgeRequestId { get; set; } -} \ No newline at end of file diff --git a/Imagekit/Models/Response/ResultTags.cs b/Imagekit/Models/Response/ResultTags.cs deleted file mode 100644 index ef73cdb3..00000000 --- a/Imagekit/Models/Response/ResultTags.cs +++ /dev/null @@ -1,12 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -[ExcludeFromCodeCoverage] -public class ResultTags : ResponseMetaData -{ - public List SuccessfullyUpDateTimeTimedfileIds { get; set; } -} \ No newline at end of file diff --git a/Imagekit/Models/ResponseMetaData.cs b/Imagekit/Models/ResponseMetaData.cs deleted file mode 100644 index 7e0f6e31..00000000 --- a/Imagekit/Models/ResponseMetaData.cs +++ /dev/null @@ -1,10 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -public class ResponseMetaData -{ - public string Raw { get; set; } - - public int HttpStatusCode { get; set; } -} \ No newline at end of file diff --git a/Imagekit/Models/TagsRequest.cs b/Imagekit/Models/TagsRequest.cs deleted file mode 100644 index 43622ea5..00000000 --- a/Imagekit/Models/TagsRequest.cs +++ /dev/null @@ -1,12 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -using System.Collections.Generic; - -public class TagsRequest -{ - public List fileIds { get; set; } - - public List tags { get; set; } -} diff --git a/Imagekit/Models/UploadTransformation.cs b/Imagekit/Models/UploadTransformation.cs deleted file mode 100644 index dadc45ec..00000000 --- a/Imagekit/Models/UploadTransformation.cs +++ /dev/null @@ -1,47 +0,0 @@ -namespace Imagekit.Models -{ - using System.Collections.Generic; - - public class PostTransformation - { - public string type { get; set; } - public string value { get; set; } - } - - public enum Protocol - { - hls, - dash - } - - public class TransformationObject : PostTransformation - { - public string type { get; } = "transformation"; - public string value { get; set; } - } - - public class GifToVideoObject : PostTransformation - { - public string type { get; } = "gif-to-video"; - public string? value { get; set; } - } - public class ThumbnailObject : PostTransformation - { - public string type { get; } = "thumbnail"; - public string? value { get; set; } - } - - public class AbsObject : PostTransformation - { - public string type { get; } = "abs"; - public string value { get; set; } - public Protocol protocol { get; set; } - } - - public class UploadTransformation - { - public string? pre { get; set; } - public List? post { get; set; } - } - -} diff --git a/Imagekit/Properties/AssemblyProperties.cs b/Imagekit/Properties/AssemblyProperties.cs deleted file mode 100644 index 7784aa5c..00000000 --- a/Imagekit/Properties/AssemblyProperties.cs +++ /dev/null @@ -1,5 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("Imagekit.UnitTests")] \ No newline at end of file diff --git a/Imagekit/RestClient.cs b/Imagekit/RestClient.cs deleted file mode 100644 index ddced72b..00000000 --- a/Imagekit/RestClient.cs +++ /dev/null @@ -1,1876 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; -using global::Imagekit.Constant; -using global::Imagekit.Helper; -using global::Imagekit.Models; -using global::Imagekit.Models.Response; -using global::Imagekit.Util; -using Imagekit; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -internal class RestClient -{ - private readonly string mediaAPIBaseUrl = UrlHandler.MediaAPIBaseUrl; - private readonly string uploadAPIBaseUrl = UrlHandler.UploadAPIBaseUrl; - private readonly HttpClient client; - - public RestClient(string privateKey, string mediaAPIBaseUrl, HttpClient httpClient) - { - if (string.IsNullOrEmpty(privateKey)) - { - throw new Exception(ErrorMessages.InvalidKey); - } - - if (string.IsNullOrEmpty(mediaAPIBaseUrl)) - { - throw new Exception(ErrorMessages.InvalidApiUrl); - } - - this.client = httpClient; - this.client.DefaultRequestHeaders.Add("Authorization", "Basic " + Utils.EncodeTo64(privateKey)); - } - - public ResponseMetaData GetFileListRequest(GetFileListRequest getFileListRequest) - { - try - { - ResultList model = new ResultList(); - string param = GetJsonBody.GetFileRequestBody(getFileListRequest); - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.GetFileRequest, param); - HttpResponseMessage response = this.client.GetAsync(url).Result; - string res = response.Content.ReadAsStringAsync().Result; - - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task GetFileListRequestAsync(GetFileListRequest getFileListRequest) - { - try - { - ResultList model = new ResultList(); - string param = GetJsonBody.GetFileRequestBody(getFileListRequest); - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.GetFileRequest, param); - - HttpResponseMessage response = await this.client.GetAsync(url); - string res = response.Content.ReadAsStringAsync().Result; - - if (res.Length > 2) - { - var token = JToken.Parse(res); - if (token is JArray) - { - model.FileList = JsonConvert.DeserializeObject>(res); - } - else - { - model = JsonConvert.DeserializeObject(res); - } - } - - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData PurgeCache(string path) - { - try - { - ResultCache model = new ResultCache(); - if (!ValidateParamater.IsValidateParam(path)) - { - throw new Exception(ErrorMessages.InvalidUrlValue); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.GetPurge); - - object data = new - { - url = path, - }; - var content = JsonConvert.SerializeObject(data); - var stringContent = - new StringContent(content, Encoding.UTF8, - "application/json"); // use MediaTypeNames.Application.Json in Core 3.0+ and Standard 2.1+ - - HttpResponseMessage response = this.client.PostAsync(url, stringContent).Result; - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task PurgeCacheAsync(string path) - { - try - { - ResultCache model = new ResultCache(); - if (!ValidateParamater.IsValidateParam(path)) - { - throw new Exception(ErrorMessages.InvalidUrlValue); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.GetPurge); - object data = new - { - url = path, - }; - var content = JsonConvert.SerializeObject(data); - var stringContent = - new StringContent(content: content, encoding: Encoding.UTF8, - mediaType: "application/json"); // use MediaTypeNames.Application.Json in Core 3.0+ and Standard 2.1+ - - HttpResponseMessage response = await this.client.PostAsync(url, stringContent); - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData PurgeStatus(string purgeRequestId) - { - try - { - ResultCacheStatus model = new ResultCacheStatus(); - if (!ValidateParamater.IsValidateParam(purgeRequestId)) - { - throw new Exception(ErrorMessages.InvalidPurgeUrl); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.GetPurgeStatus, purgeRequestId); - - HttpResponseMessage response = this.client.GetAsync(url).Result; - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task PurgeStatusAsync(string purgeRequestId) - { - try - { - ResultCacheStatus model = new ResultCacheStatus(); - if (!ValidateParamater.IsValidateParam(purgeRequestId)) - { - throw new Exception(ErrorMessages.InvalidPurgeUrl); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.GetPurgeStatus, purgeRequestId); - - HttpResponseMessage response = await this.client.GetAsync(url); - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData GetFileDetail(string fileId) - { - try - { - Result model = new Result(); - if (!ValidateParamater.IsValidateParam(fileId)) - { - throw new Exception(ErrorMessages.FileIdMissing); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.GetFileDetails, fileId); - - HttpResponseMessage response = this.client.GetAsync(url).Result; - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task GetFileDetailAsync(string fileId) - { - try - { - Result model = new Result(); - if (!ValidateParamater.IsValidateParam(fileId)) - { - throw new Exception(ErrorMessages.FileIdMissing); - } - - Dictionary headers = Utils.GetHeaders(); - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.GetFileDetails, fileId); - - HttpResponseMessage response = await this.client.GetAsync(url); - string res = response.Content.ReadAsStringAsync().Result; - - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData Upload(FileCreateRequest fileCreateRequest) - { - try - { - Result model = new Result(); - string validate = ValidateParamater.IsValidateUpload(fileCreateRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = this.uploadAPIBaseUrl + UrlHandler.UploadFile; - Dictionary headers = Utils.GetHeaders(); - var formdata = MultipartFormDataModel.Build(fileCreateRequest); - HttpResponseMessage response = this.client.PostAsync(url, formdata).Result; - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - /// - /// - /// - /// - /// A representing the result of the asynchronous operation. - public async Task UploadAsync(FileCreateRequest fileCreateRequest) - { - try - { - Result model = new Result(); - string validate = ValidateParamater.IsValidateUpload(fileCreateRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = this.uploadAPIBaseUrl + UrlHandler.UploadFile; - Dictionary headers = Utils.GetHeaders(); - var formdata = MultipartFormDataModel.Build(fileCreateRequest); - - HttpResponseMessage response = await this.client.PostAsync(url, formdata); - string res = response.Content.ReadAsStringAsync().Result; - - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData UpdateFileDetail(FileUpdateRequest fileUpdateRequest) - { - try - { - Result model = new Result(); - string validate = ValidateParamater.IsValidateUpdateFile(fileUpdateRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.UpdateFileRequest, fileUpdateRequest.fileId); - Dictionary headers = Utils.GetHeaders(); - - var content = JsonConvert.SerializeObject(fileUpdateRequest, - new JsonSerializerSettings() - { - NullValueHandling = NullValueHandling.Ignore, - }); - var stringContent = - new StringContent(content, Encoding.UTF8, - mediaType: "application/json"); - var request = new HttpRequestMessage - { - Method = new HttpMethod("PATCH"), - RequestUri = new Uri(url), - Content = stringContent, - }; - - HttpResponseMessage response = this.client.SendAsync(request).Result; - - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task UpdateFileDetailAsync(FileUpdateRequest fileUpdateRequest) - { - try - { - Result model = new Result(); - string validate = ValidateParamater.IsValidateUpdateFile(fileUpdateRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.UpdateFileRequest, fileUpdateRequest.fileId); - var content = JsonConvert.SerializeObject(fileUpdateRequest, - new JsonSerializerSettings() - { - NullValueHandling = NullValueHandling.Ignore, - }); - var stringContent = - new StringContent(content, Encoding.UTF8, - mediaType: "application/json"); - var request = new HttpRequestMessage - { - Method = new HttpMethod("PATCH"), - RequestUri = new Uri(url), - Content = stringContent, - }; - - HttpResponseMessage response = await this.client.SendAsync(request); - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData DeleteFile(string fileId) - { - try - { - ResultDelete model = new ResultDelete(); - if (!ValidateParamater.IsValidateParam(fileId)) - { - throw new Exception(ErrorMessages.FileIdMissing); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.DeleteFile, fileId); - - HttpResponseMessage response = this.client.DeleteAsync(url).Result; - string res = response.Content.ReadAsStringAsync().Result; - model.Raw = res; - model.fileId = fileId; - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - /// - /// - /// - /// - /// A representing the result of the asynchronous operation. - public async Task DeleteFileAsync(string fileId) - { - try - { - ResultDelete model = new ResultDelete(); - if (!ValidateParamater.IsValidateParam(fileId)) - { - throw new Exception(ErrorMessages.FileIdMissing); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.DeleteFile, fileId); - - HttpResponseMessage response = await this.client.DeleteAsync(url); - string res = response.Content.ReadAsStringAsync().Result; - model.Raw = res; - model.fileId = fileId; - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData BulkDeleteFiles(List fileIds) - { - try - { - ResultFileDelete model = new ResultFileDelete(); - if (!ValidateParamater.IsListCheck(fileIds)) - { - throw new Exception(ErrorMessages.ListFilesInputMissing); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.BulkDelete); - - object data = new - { - fileIds = fileIds, - }; - var content = JsonConvert.SerializeObject(data); - var stringContent = - new StringContent(content, Encoding.UTF8, - "application/json"); // use MediaTypeNames.Application.Json in Core 3.0+ and Standard 2.1+ - - HttpResponseMessage response = this.client.PostAsync(url, stringContent).Result; - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - /// - /// - /// - /// - /// A representing the result of the asynchronous operation. - public async Task BulkDeleteFilesAsync(List fileIds) - { - try - { - ResultFileDelete model = new ResultFileDelete(); - if (!ValidateParamater.IsListCheck(fileIds)) - { - throw new Exception(ErrorMessages.ListFilesInputMissing); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.BulkDelete); - - object data = new - { - fileIds = fileIds, - }; - var content = JsonConvert.SerializeObject(data); - var stringContent = - new StringContent(content, Encoding.UTF8, - mediaType: "application/json"); // use MediaTypeNames.Application.Json in Core 3.0+ and Standard 2.1+ - - HttpResponseMessage response = await this.client.PostAsync(url, stringContent); - string res = response.Content.ReadAsStringAsync().Result; - - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData GetFileMetaData(string fileId) - { - try - { - ResultMetaData model = new ResultMetaData(); - if (!ValidateParamater.IsValidateParam(fileId)) - { - throw new Exception(ErrorMessages.FileIdMissing); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.GetMetaData, fileId); - - HttpResponseMessage response = this.client.GetAsync(url).Result; - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task GetFileMetaDataAsync(string fileId) - { - try - { - ResultMetaData model = new ResultMetaData(); - if (!ValidateParamater.IsValidateParam(fileId)) - { - throw new Exception(ErrorMessages.FileIdMissing); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.GetMetaData, fileId); - - HttpResponseMessage response = await this.client.GetAsync(url); - string res = response.Content.ReadAsStringAsync().Result; - - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData GetRemoteFileMetaData(string url) - { - try - { - ResultMetaData model = new ResultMetaData(); - if (!ValidateParamater.IsValidateParam(url)) - { - throw new Exception(ErrorMessages.InvalidUrlValue); - } - - string apIurl = string.Format(this.mediaAPIBaseUrl + UrlHandler.GetRemoteData, url); - - HttpResponseMessage response = this.client.GetAsync(apIurl).Result; - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task GetRemoteFileMetaDataAsync(string url) - { - try - { - ResultMetaData model = new ResultMetaData(); - if (!ValidateParamater.IsValidateParam(url)) - { - throw new Exception(ErrorMessages.InvalidUrlValue); - } - - string apIurl = string.Format(this.mediaAPIBaseUrl + UrlHandler.GetRemoteData, url); - - HttpResponseMessage response = await this.client.GetAsync(apIurl); - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData ManageTags(TagsRequest tagsRequest, string action) - { - try - { - ResultTags model = new ResultTags(); - var validate = ValidateParamater.IsValidateTagRequest(tagsRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Empty; - - if (action == "addTags") - { - url = this.mediaAPIBaseUrl + UrlHandler.AddTags; - } - else if (action == "removeTags") - { - url = this.mediaAPIBaseUrl + UrlHandler.RemoveTags; - } - - var content = JsonConvert.SerializeObject(tagsRequest); - var stringContent = new StringContent(content, Encoding.UTF8, "application/json"); - HttpResponseMessage response = this.client.PostAsync(url, stringContent).Result; - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task ManageTagsAsync(TagsRequest tagsRequest, string action) - { - try - { - ResultTags model = new ResultTags(); - var validate = ValidateParamater.IsValidateTagRequest(tagsRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Empty; - - if (action == "addTags") - { - url = this.uploadAPIBaseUrl + UrlHandler.AddTags; - } - else if (action == "removeTags") - { - url = this.uploadAPIBaseUrl + UrlHandler.RemoveTags; - } - - var content = JsonConvert.SerializeObject(tagsRequest); - var stringContent = new StringContent(content, Encoding.UTF8, "application/json"); - HttpResponseMessage response = await this.client.PostAsync(url, stringContent); - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData RemoveAITags(AITagsRequest aITagsRequest) - { - try - { - ResultTags model = new ResultTags(); - var validate = ValidateParamater.IsValidateAiTagRequest(aITagsRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - // removeAITags - string url = string.Empty; - - url = this.mediaAPIBaseUrl + UrlHandler.RemoveAITags; - - var content = JsonConvert.SerializeObject(aITagsRequest); - var stringContent = new StringContent(content, Encoding.UTF8, "application/json"); - HttpResponseMessage response = this.client.PostAsync(url, stringContent).Result; - string res = response.Content.ReadAsStringAsync().Result; - - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task RemoveAITagsAsync(AITagsRequest aITagsRequest) - { - try - { - ResultTags model = new ResultTags(); - var validate = ValidateParamater.IsValidateAiTagRequest(aITagsRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - // removeAITags - string url = string.Empty; - - url = this.uploadAPIBaseUrl + UrlHandler.RemoveAITags; - - var content = JsonConvert.SerializeObject(aITagsRequest); - var stringContent = new StringContent(content, Encoding.UTF8, "application/json"); - HttpResponseMessage response = await this.client.PostAsync(url, stringContent); - string res = response.Content.ReadAsStringAsync().Result; - - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData GetCustomMetaDataFields(bool? includeDeleted = false) - { - try - { - ResultCustomMetaDataFieldList model = new ResultCustomMetaDataFieldList(); - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.CustomMetadataFields, includeDeleted); - - HttpResponseMessage response = this.client.GetAsync(url).Result; - string res = response.Content.ReadAsStringAsync().Result; - if (res.Length > 2) - { - var token = JToken.Parse(res); - if (token is JArray) - { - model.ResultCustomMetaDataFieldList1 = JsonConvert.DeserializeObject>(res); - } - else - { - model = JsonConvert.DeserializeObject(res); - } - } - - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task GetCustomMetaDataFieldsAsync(bool includeDeleted) - { - try - { - ResultCustomMetaDataFieldList model = new ResultCustomMetaDataFieldList(); - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.CustomMetadataFields, includeDeleted); - - HttpResponseMessage response = await this.client.GetAsync(url); - string res = response.Content.ReadAsStringAsync().Result; - - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData CreateCustomMetaDataFields( - CustomMetaDataFieldCreateRequest customMetaDataFieldCreateRequest) - { - try - { - ResultCustomMetaDataField model = new ResultCustomMetaDataField(); - var validate = - ValidateParamater.IsValidateCustomMetaDataFieldCreateRequest(customMetaDataFieldCreateRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Empty; - url = this.mediaAPIBaseUrl + UrlHandler.CreareCustomMetaDataFields; - - string body = GetJsonBody.CreateCustomMetaDataBody(customMetaDataFieldCreateRequest); - var stringContent = new StringContent(body, Encoding.UTF8, "application/json"); - HttpResponseMessage response = this.client.PostAsync(url, stringContent).Result; - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task CreateCustomMetaDataFieldsAsync( - CustomMetaDataFieldCreateRequest customMetaDataFieldCreateRequest) - { - try - { - ResultCustomMetaDataField model = new ResultCustomMetaDataField(); - var validate = - ValidateParamater.IsValidateCustomMetaDataFieldCreateRequest(customMetaDataFieldCreateRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Empty; - url = this.mediaAPIBaseUrl + UrlHandler.CreareCustomMetaDataFields; - - string body = GetJsonBody.CreateCustomMetaDataBody(customMetaDataFieldCreateRequest); - var stringContent = new StringContent(body, Encoding.UTF8, "application/json"); - HttpResponseMessage response = await this.client.PostAsync(url, stringContent); - string res = response.Content.ReadAsStringAsync().Result; - - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData DeleteCustomMetaDataField(string id) - { - try - { - ResultNoContent model = new ResultNoContent(); - if (!ValidateParamater.IsValidateParam(id)) - { - throw new Exception(ErrorMessages.InvalidfileIdsValue); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.DeleteCustomMetaDataFields, id); - - HttpResponseMessage response = this.client.DeleteAsync(url).Result; - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task DeleteCustomMetaDataFieldAsync(string id) - { - try - { - ResultNoContent model = new ResultNoContent(); - if (!ValidateParamater.IsValidateParam(id)) - { - throw new Exception(ErrorMessages.InvalidfileIdsValue); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.DeleteCustomMetaDataFields, id); - - HttpResponseMessage response = await this.client.DeleteAsync(url); - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData UpdateCustomMetaDataFields( - CustomMetaDataFieldUpdateRequest customMetaDataFieldUpdateRequest) - { - try - { - ResultCustomMetaDataField model = new ResultCustomMetaDataField(); - var validate = - ValidateParamater.IsValidateCustomMetaDataFieldUpdateRequest(customMetaDataFieldUpdateRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Empty; - url = string.Format( - this.mediaAPIBaseUrl + UrlHandler.UpdateCustomMetadataFields, - customMetaDataFieldUpdateRequest.Id); - string body = GetJsonBody.UpdateCustomMetaDataBody(customMetaDataFieldUpdateRequest); - - var stringContent = - new StringContent(body, Encoding.UTF8, - mediaType: "application/json"); - var request = new HttpRequestMessage - { - Method = new HttpMethod("PATCH"), - RequestUri = new Uri(url), - Content = stringContent, - }; - HttpResponseMessage response = this.client.SendAsync(request).Result; - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task UpdateCustomMetaDataFieldsAsync( - CustomMetaDataFieldUpdateRequest customMetaDataFieldUpdateRequest) - { - try - { - ResultCustomMetaDataField model = new ResultCustomMetaDataField(); - var validate = - ValidateParamater.IsValidateCustomMetaDataFieldUpdateRequest(customMetaDataFieldUpdateRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Empty; - url = string.Format( - this.mediaAPIBaseUrl + UrlHandler.UpdateCustomMetadataFields, - customMetaDataFieldUpdateRequest.Id); - string body = GetJsonBody.UpdateCustomMetaDataBody(customMetaDataFieldUpdateRequest); - - var stringContent = - new StringContent(body, Encoding.UTF8, - mediaType: "application/json"); - var request = new HttpRequestMessage - { - Method = new HttpMethod("PATCH"), - RequestUri = new Uri(url), - Content = stringContent, - }; - HttpResponseMessage response = await this.client.SendAsync(request); - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData DeleteFileVersion(DeleteFileVersionRequest deleteFileVersionRequest) - { - try - { - ResultNoContent model = new ResultNoContent(); - var validate = ValidateParamater.IsValidateDeleteFileVersionRequest(deleteFileVersionRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.DeleteVesrion, deleteFileVersionRequest.fileId, - deleteFileVersionRequest.versionId); - - HttpResponseMessage response = this.client.DeleteAsync(url).Result; - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task DeleteFileVersionAsync(DeleteFileVersionRequest deleteFileVersionRequest) - { - try - { - ResultNoContent model = new ResultNoContent(); - var validate = ValidateParamater.IsValidateDeleteFileVersionRequest(deleteFileVersionRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.DeleteVesrion, deleteFileVersionRequest.fileId, - deleteFileVersionRequest.versionId); - - HttpResponseMessage response = await this.client.DeleteAsync(url); - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData CopyFile(CopyFileRequest copyFileRequest) - { - try - { - ResultNoContent model = new ResultNoContent(); - string validate = ValidateParamater.IsValidateCopyRequest(copyFileRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Empty; - url = string.Format(this.mediaAPIBaseUrl + UrlHandler.CopyFile); - var content = JsonConvert.SerializeObject(copyFileRequest); - var stringContent = new StringContent(content, Encoding.UTF8, "application/json"); - HttpResponseMessage response = this.client.PostAsync(url, stringContent).Result; - string res = response.Content.ReadAsStringAsync().Result; - - model = JsonConvert.DeserializeObject(res); - - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task CopyFileAsync(CopyFileRequest copyFileRequest) - { - try - { - ResultNoContent model = new ResultNoContent(); - string validate = ValidateParamater.IsValidateCopyRequest(copyFileRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Empty; - url = string.Format(this.mediaAPIBaseUrl + UrlHandler.CopyFile); - var content = JsonConvert.SerializeObject(copyFileRequest); - var stringContent = new StringContent(content, Encoding.UTF8, "application/json"); - HttpResponseMessage response = await this.client.PostAsync(url, stringContent); - string res = response.Content.ReadAsStringAsync().Result; - - model = JsonConvert.DeserializeObject(res); - - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData MoveFile(MoveFileRequest moveFileRequest) - { - try - { - ResultNoContent model = new ResultNoContent(); - string validate = ValidateParamater.IsValidateMoveRequest(moveFileRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Empty; - url = string.Format(this.mediaAPIBaseUrl + UrlHandler.MoveFile); - var content = JsonConvert.SerializeObject(moveFileRequest); - var stringContent = new StringContent(content, Encoding.UTF8, "application/json"); - HttpResponseMessage response = this.client.PostAsync(url, stringContent).Result; - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task MoveFileAsync(MoveFileRequest moveFileRequest) - { - try - { - ResultNoContent model = new ResultNoContent(); - string validate = ValidateParamater.IsValidateMoveRequest(moveFileRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Empty; - url = string.Format(this.mediaAPIBaseUrl + UrlHandler.MoveFile); - var content = JsonConvert.SerializeObject(moveFileRequest); - var stringContent = new StringContent(content, Encoding.UTF8, "application/json"); - HttpResponseMessage response = await this.client.PostAsync(url, stringContent); - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData RenameFile(RenameFileRequest renameFileRequest) - { - try - { - ResultRenameFile model = new ResultRenameFile(); - var validate = ValidateParamater.IsValidateRenameRequest(renameFileRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Empty; - url = string.Format(this.mediaAPIBaseUrl + UrlHandler.RenameFile); - var content = JsonConvert.SerializeObject(renameFileRequest); - var stringContent = new StringContent(content, Encoding.UTF8, "application/json"); - HttpResponseMessage response = this.client.PutAsync(url, stringContent).Result; - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task RenameFileAsync(RenameFileRequest renameFileRequest) - { - try - { - ResultRenameFile model = new ResultRenameFile(); - var validate = ValidateParamater.IsValidateRenameRequest(renameFileRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Empty; - url = string.Format(this.mediaAPIBaseUrl + UrlHandler.RenameFile); - var content = JsonConvert.SerializeObject(renameFileRequest); - var stringContent = new StringContent(content, Encoding.UTF8, "application/json"); - HttpResponseMessage response = await this.client.PutAsync(url, stringContent); - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData CreateFolder(CreateFolderRequest createFolderRequest) - { - try - { - ResultEmptyBlock model = new ResultEmptyBlock(); - var isValidateFolder = ValidateParamater.IsValidateFolder(createFolderRequest); - if (!string.IsNullOrEmpty(isValidateFolder)) - { - throw new Exception(isValidateFolder); - } - - string url = string.Empty; - url = string.Format(this.mediaAPIBaseUrl + UrlHandler.CreateFolder); - var content = JsonConvert.SerializeObject(createFolderRequest); - Console.WriteLine(content); - var stringContent = new StringContent(content, Encoding.UTF8, "application/json"); - HttpResponseMessage response = this.client.PostAsync(url, stringContent).Result; - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task CreateFolderAsync(CreateFolderRequest createFolderRequest) - { - try - { - ResultEmptyBlock model = new ResultEmptyBlock(); - var isValidateFolder = ValidateParamater.IsValidateFolder(createFolderRequest); - if (!string.IsNullOrEmpty(isValidateFolder)) - { - throw new Exception(isValidateFolder); - } - - string url = string.Empty; - url = string.Format(this.mediaAPIBaseUrl + UrlHandler.CreateFolder); - var content = JsonConvert.SerializeObject(createFolderRequest); - var stringContent = new StringContent(content, Encoding.UTF8, "application/json"); - HttpResponseMessage response = await this.client.PostAsync(url, stringContent); - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData DeleteFolder(DeleteFolderRequest deleteFolderRequest) - { - try - { - ResultNoContent model = new ResultNoContent(); - if (!ValidateParamater.IsValidateDeleteFolder(deleteFolderRequest)) - { - throw new Exception(ErrorMessages.InvalidDelFolderValue); - } - - var body = GetJsonBody.DeleteFolderBody(deleteFolderRequest); - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.DeleteFolder); - var request = new HttpRequestMessage - { - Method = HttpMethod.Delete, - RequestUri = new Uri(url), - Content = new StringContent(body, Encoding.UTF8, "application/json"), - }; - - HttpResponseMessage response = this.client.SendAsync(request).Result; - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task DeleteFolderAsync(DeleteFolderRequest deleteFolderRequest) - { - try - { - ResultNoContent model = new ResultNoContent(); - if (!ValidateParamater.IsValidateDeleteFolder(deleteFolderRequest)) - { - throw new Exception(ErrorMessages.InvalidDelFolderValue); - } - - var body = GetJsonBody.DeleteFolderBody(deleteFolderRequest); - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.DeleteFolder); - var request = new HttpRequestMessage - { - Method = HttpMethod.Delete, - RequestUri = new Uri(url), - Content = new StringContent(body, Encoding.UTF8, "application/json"), - }; - - HttpResponseMessage response = await this.client.SendAsync(request); - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData CopyFolder(CopyFolderRequest copyFolderRequest) - { - try - { - ResultOfFolderActions model = new ResultOfFolderActions(); - var validate = ValidateParamater.IsValidateCopyFolder(copyFolderRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.CopyFolder); - var content = JsonConvert.SerializeObject(copyFolderRequest); - var stringContent = - new StringContent(content, Encoding.UTF8, - "application/json"); // use MediaTypeNames.Application.Json in Core 3.0+ and Standard 2.1+ - - HttpResponseMessage response = this.client.PostAsync(url, stringContent).Result; - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task CopyFolderAsync(CopyFolderRequest copyFolderRequest) - { - try - { - ResultOfFolderActions model = new ResultOfFolderActions(); - var validate = ValidateParamater.IsValidateCopyFolder(copyFolderRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.CopyFolder); - var content = JsonConvert.SerializeObject(copyFolderRequest); - var stringContent = - new StringContent(content, Encoding.UTF8, - "application/json"); // use MediaTypeNames.Application.Json in Core 3.0+ and Standard 2.1+ - - HttpResponseMessage response = await this.client.PostAsync(url, stringContent); - string res = response.Content.ReadAsStringAsync().Result; - - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData MoveFolder(MoveFolderRequest moveFolderRequest) - { - try - { - ResultOfFolderActions model = new ResultOfFolderActions(); - var validate = ValidateParamater.IsValidateMoveFolder(moveFolderRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.MoveFolder); - var content = JsonConvert.SerializeObject(moveFolderRequest); - var stringContent = - new StringContent(content, Encoding.UTF8, - "application/json"); // use MediaTypeNames.Application.Json in Core 3.0+ and Standard 2.1+ - - HttpResponseMessage response = this.client.PostAsync(url, stringContent).Result; - string res = response.Content.ReadAsStringAsync().Result; - - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task MoveFolderAsync(MoveFolderRequest moveFolderRequest) - { - try - { - ResultOfFolderActions model = new ResultOfFolderActions(); - - var validate = ValidateParamater.IsValidateMoveFolder(moveFolderRequest); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.MoveFolder); - var content = JsonConvert.SerializeObject(moveFolderRequest); - var stringContent = - new StringContent(content, Encoding.UTF8, - "application/json"); // use MediaTypeNames.Application.Json in Core 3.0+ and Standard 2.1+ - - HttpResponseMessage response = await this.client.PostAsync(url, stringContent); - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData GetBulkJobStatus(string jobId) - { - try - { - ResultBulkJobStatus model = new ResultBulkJobStatus(); - if (!ValidateParamater.IsValidateParam(jobId)) - { - throw new Exception(ErrorMessages.InvalidJobValue); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.GetJobStatus, jobId); - - HttpResponseMessage response = this.client.GetAsync(url).Result; - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task GetBulkJobStatusAsync(string jobId) - { - try - { - ResultBulkJobStatus model = new ResultBulkJobStatus(); - if (!ValidateParamater.IsValidateParam(jobId)) - { - throw new Exception(ErrorMessages.InvalidJobValue); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.GetJobStatus, jobId); - - HttpResponseMessage response = await this.client.GetAsync(url); - string res = response.Content.ReadAsStringAsync().Result; - - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData GetFileVersions(string fileId) - { - try - { - ResultFileVersions model = new ResultFileVersions(); - if (!ValidateParamater.IsValidateParam(fileId)) - { - throw new Exception(ErrorMessages.FileIdMissing); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.GetFileVersion, fileId); - HttpResponseMessage response = this.client.GetAsync(url).Result; - string res = response.Content.ReadAsStringAsync().Result; - var token = JToken.Parse(res); - if (token is JArray) - { - model.ResultFileVersionDetailsList = JsonConvert.DeserializeObject>(res); - } - - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task GetFileVersionsAsync(string fileId) - { - try - { - ResultFileVersions model = new ResultFileVersions(); - if (!ValidateParamater.IsValidateParam(fileId)) - { - throw new Exception(ErrorMessages.FileIdMissing); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.GetFileVersion, fileId); - HttpResponseMessage response = this.client.GetAsync(url).Result; - string res = await response.Content.ReadAsStringAsync(); - var token = JToken.Parse(res); - if (token is JArray) - { - model.ResultFileVersionDetailsList = JsonConvert.DeserializeObject>(res); - } - - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData GetFileVersionDetails(string fileId, string versionId) - { - try - { - ResultFileVersionDetails model = new ResultFileVersionDetails(); - var validate = ValidateParamater.IsValidateParam(fileId, versionId); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.GetFileVersionDetail, fileId, versionId); - HttpResponseMessage response = this.client.GetAsync(url).Result; - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task GetFileVersionDetailsAsync(string fileId, string versionId) - { - try - { - ResultFileVersionDetails model = new ResultFileVersionDetails(); - var validate = ValidateParamater.IsValidateParam(fileId, versionId); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.GetFileVersionDetail, fileId, versionId); - HttpResponseMessage response = await this.client.GetAsync(url); - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public ResponseMetaData RestoreFileVersion(string fileId, string versionId) - { - try - { - Result model = new Result(); - var validate = ValidateParamater.IsValidateParam(fileId, versionId); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.RestoreVesrion, fileId, versionId); - HttpResponseMessage response = this.client.PutAsync(url, null).Result; - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } - - public async Task RestoreFileVersionAsync(string fileId, string versionId) - { - try - { - Result model = new Result(); - var validate = ValidateParamater.IsValidateParam(fileId, versionId); - if (!string.IsNullOrEmpty(validate)) - { - throw new Exception(validate); - } - - string url = string.Format(this.mediaAPIBaseUrl + UrlHandler.RestoreVesrion, fileId, versionId); - HttpResponseMessage response = await this.client.PutAsync(url, null); - string res = response.Content.ReadAsStringAsync().Result; - model = JsonConvert.DeserializeObject(res); - Utils.PopulateResponseMetadata( - res, - model, - Convert.ToInt32(response.StatusCode), - responseHeaders: null); - return model; - } - catch (Exception ex) - { - throw new ImagekitException(ex.Message); - } - } -} \ No newline at end of file diff --git a/Imagekit/Util/Calculation.cs b/Imagekit/Util/Calculation.cs deleted file mode 100644 index 03a86129..00000000 --- a/Imagekit/Util/Calculation.cs +++ /dev/null @@ -1,47 +0,0 @@ -namespace Imagekit.Util -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using System.Numerics; - using System.Text; - using System.Text.RegularExpressions; - using System.Threading.Tasks; - - public class Calculation - { - public static int GetHammingDistance(string firstHex, string secondHex) - { - if (!(IsValidHex(firstHex) && IsValidHex(secondHex))) - { - throw new Exception("Both argument should be hexadecimal."); - } - - if (firstHex.Length != secondHex.Length) - { - throw new Exception("Both argument are not equal in length."); - } - - BigInteger b1 = BigInteger.Parse(firstHex, NumberStyles.HexNumber); - BigInteger b2 = BigInteger.Parse(secondHex, NumberStyles.HexNumber); - BigInteger xor = b1 ^ b2; - - int count = 0; - for (int i = 0; i < xor.ToString().Length; i++) - { - if (xor.ToString()[i] == '1') - { - count += 1; - } - } - - return count; - } - - private static bool IsValidHex(string hex) - { - return System.Text.RegularExpressions.Regex.IsMatch(hex, @"^[0-9a-fA-F]+$"); - } - } -} diff --git a/Imagekit/Util/Constants.cs b/Imagekit/Util/Constants.cs deleted file mode 100644 index 07007431..00000000 --- a/Imagekit/Util/Constants.cs +++ /dev/null @@ -1,26 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit.Util -{ - public static class Constants - { - public const string TransformationParameter = "tr"; - public const string SignatureParameter = "ik-s"; - public const string TimestampParameter = "ik-t"; - public const string ChainTransformDelimiter = ":"; - public const string TransformDelimiter = ","; - public const string TransformKeyValueDelimiter = "-"; - public const string DefaultTimestamp = "9999999999"; - public const string ProtocolQuery = @"/http[s]?\:\/\//"; - - public const string HttpsProtocol = "https://"; - public const string HttpProtocol = "http://"; - public const string ApiHost = "api.imagekit.io"; - public const string UploadApiHost = "dasdasd.sadsdasd.io"; - public const string FileApi = "/v1/files"; - public const string UploadApi = "/api/v1/files/upload"; - public const string SdkVersion = "5.0.0"; - } -} \ No newline at end of file diff --git a/Imagekit/Util/Utils.cs b/Imagekit/Util/Utils.cs deleted file mode 100644 index 321c510b..00000000 --- a/Imagekit/Util/Utils.cs +++ /dev/null @@ -1,86 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace Imagekit.Util -{ - using System; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.IO; - using System.Linq; - using System.Security.Cryptography; - using System.Text; - - [ExcludeFromCodeCoverage] - public static class Utils - { - public static string ListToString(List list) - { - return string.Join(",", list); - } - - public static string ListToString(string[] list) - { - return string.Join(",", list); - } - - public static Dictionary GetHeaders() - { - Dictionary headers = new Dictionary - { - { "Accept-Encoding", "application/json" }, - { "Content-Type", "application/json" }, - { "Authorization", "Basic " + "private_FguYxKgB8/Jm9Xs5ZyyLfwIBSFU=" }, - }; - return headers; - } - - public static ResponseMetaData PopulateResponseMetadata( - string respBody, - ResponseMetaData responseMetadata, - int responseCode, - Dictionary> responseHeaders) - { - if (responseMetadata == null) - { - responseMetadata = new ResponseMetaData(); - } - - responseMetadata.Raw = respBody; - responseMetadata.HttpStatusCode = responseCode; - return responseMetadata; - } - - public static long ToUnixTime(DateTime dateTime) - { - return (int)dateTime.ToUniversalTime().Subtract(new DateTime(1970, 1, 1)).TotalSeconds; - } - - public static string EncodeTo64(string toEncode) - { - var plainTextBytes = Encoding.UTF8.GetBytes(toEncode + ":"); - string returnValue = Convert.ToBase64String(plainTextBytes); - return returnValue; - } - - public static string GetSignatureTimestamp(int seconds) - { - if (seconds <= 0) - { - return Constants.DefaultTimestamp; - } - - var timeStamp = ToUnixTime(DateTime.UtcNow); - return (timeStamp + seconds).ToString(); - } - - public static string CalculateSignature(string input, byte[] key) - { - HMACSHA1 myhmacsha1 = new HMACSHA1(key); - byte[] byteArray = Encoding.ASCII.GetBytes(input); - MemoryStream stream = new MemoryStream(byteArray); - return myhmacsha1.ComputeHash(stream).Aggregate(string.Empty, (s, e) => s + string.Format("{0:x2}", e), s => s); - } - } -} \ No newline at end of file diff --git a/Imagekit/stylecop.json b/Imagekit/stylecop.json deleted file mode 100644 index 2bb8814b..00000000 --- a/Imagekit/stylecop.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - // ACTION REQUIRED: This file was automatically added to your project, but it - // will not take effect until additional steps are taken to enable it. See the - // following page for additional informations: - // - // https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md - - "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", - "settings": { - "documentationRules": { - "companyName": "Imagekit" - } - } -} \ No newline at end of file diff --git a/LICENSE b/LICENSE index 1247a613..20278610 100644 --- a/LICENSE +++ b/LICENSE @@ -1,22 +1,201 @@ -MIT License + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -Copyright (c) 2017 Imagekit + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -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: + 1. Definitions. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. -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. + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2026 Image Kit + + 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/README.md b/README.md index 7442f66c..fe964e0b 100644 --- a/README.md +++ b/README.md @@ -1,962 +1,674 @@ -[ImageKit.io](https://imagekit.io) - -## DotNET (NET45/Standard/Core) SDK for ImageKit - -[![CI Pipeline](https://github.com/imagekit-developer/imagekit-dotnet/workflows/CI%20Pipeline/badge.svg?branch=master)](https://github.com/imagekit-developer/imagekit-dotnet) [![NuGet](https://img.shields.io/nuget/v/imagekit.svg)](https://www.nuget.org/packages/Imagekit) [![codecov](https://codecov.io/gh/imagekit-developer/imagekit-dotnet/branch/master/graph/badge.svg)](https://codecov.io/gh/imagekit-developer/imagekit-dotnet) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Twitter Follow](https://img.shields.io/twitter/follow/imagekitio?label=Follow&style=social)](https://twitter.com/ImagekitIo) - -ImageKit DotNET SDK allows you to use [image resizing](https://docs.imagekit.io/features/image-transformations), [optimization](https://docs.imagekit.io/features/image-optimization), [file uploading](https://docs.imagekit.io/api-reference/upload-file-api) and other [ImageKit APIs](https://docs.imagekit.io/api-reference/api-introduction) from applications written in server-side C#. - -##### Table of contents - -* [Installation](#installation) -* [Initialization](#initialization) -* [URL generation](#url-generation) -* [File Upload](#file-upload) -* [File Management](#file-management) -* [Tags](#tags-management) -* [Manage Folder](#folder-management) -* [Bulk Job Status](#job-management) -* [Purge](#purge) -* [Meta Data](#metadata) -* [Utility functions](#utility-functions) -* [Support](#support) -* [Links](#links) +# ImageKit.io C# SDK + +The ImageKit C# SDK is a comprehensive library designed to simplify the integration of ImageKit into your server-side applications. It provides powerful tools for working with the [ImageKit REST API](https://imagekit.io/docs/api-reference), including building and transforming URLs, generating signed URLs for secure content delivery, and handling file uploads. + +For additional details, refer to the [ImageKit REST API documentation](https://imagekit.io/docs/api-reference). + +## Table of Contents + +- [Installation](#installation) +- [Requirements](#requirements) +- [Usage](#usage) +- [Client configuration](#client-configuration) +- [Requests and responses](#requests-and-responses) +- [Raw responses](#raw-responses) +- [URL generation](#url-generation) + - [Basic URL generation](#basic-url-generation) + - [URL generation with transformations](#url-generation-with-transformations) + - [URL generation with image overlay](#url-generation-with-image-overlay) + - [URL generation with text overlay](#url-generation-with-text-overlay) + - [URL generation with multiple overlays](#url-generation-with-multiple-overlays) + - [Signed URLs for secure delivery](#signed-urls-for-secure-delivery) + - [Using Raw transformations for undocumented features](#using-raw-transformations-for-undocumented-features) +- [Authentication parameters for client-side uploads](#authentication-parameters-for-client-side-uploads) +- [Webhook verification](#webhook-verification) +- [Error handling](#error-handling) +- [Network options](#network-options) + - [Retries](#retries) + - [Timeouts](#timeouts) + - [Proxies](#proxies) +- [Undocumented API functionality](#undocumented-api-functionality) +- [Semantic versioning](#semantic-versioning) ## Installation -Package Manager - -```cs -Install-Package Imagekit -``` - -PackageReference - -```html - - -``` - -.Net CLI - -```cs -dotnet add package Imagekit --version 4.0.1 -``` - -Open up your project, navigate to the Nuget package manager console, and add the Imagekit package. -Also, you can search for [Imagekit](https://www.nuget.org/packages/Imagekit) in Nuget GUI. - -## Initialization - -Add this reference where you want to use imagekit.io services: - -```cs -using Imagekit; +Install the package from [NuGet](https://www.nuget.org/packages/Imagekit): -ImageKitClient imagekit = new ImageKitClient(publicKey, privateKey, urlEndPoint); +```bash +dotnet add package Imagekit ``` -**Note**: You can get the `apiKey`, `apiSecret`, and ImagekitId from your [Imagekit.io dashboard](https://imagekit.io/dashboard). +## Requirements -## Demo application - -The fastest way to get started is by running the demo application in the [ImageKitSample](/ImageKitSample) folder. +This library requires .NET Standard 2.0 or later. ## Usage -You can use this DotNET SDK for three different functions: URL generation, file uploads, and file management. The usage of the SDK has been explained below. - -### URL Generation +```csharp +using System; +using Imagekit; +using Imagekit.Models.Files; -**1\. Using image path and image hostname or endpoint** +ImageKitClient client = new() +{ + PrivateKey = "private_key_xxx", +}; -This method allows you to create a URL using the `path` where the image exists and the URL endpoint (`urlEndpoint`) you want to use to access the image. You can refer to the documentation [here](https://docs.imagekit.io/integration/url-endpoints) to read more about URL endpoints in ImageKit and the section about [image origins](https://docs.imagekit.io/integration/configure-origin) to understand paths with different kinds of origins. +FileUploadParams parameters = new() +{ + File = new System.IO.FileStream("/path/to/your/image.jpg", System.IO.FileMode.Open), + FileName = "uploaded-image.jpg", +}; -```cs -string path = "/default_image.jpg"; -Transformation trans = new Transformation() -.Width(400) -.Height(300) -.AspectRatio("4-3") -.Quality(40) -.Crop("force") -.CropMode("extract") -.Focus("left") -.Format("jpeg") -.Raw("l-text,i-Imagekit,fs-50,l-end"); +var response = await client.Files.Upload(parameters); -string imageURL = imagekit.Url(trans).Path(path).TransformationPosition("query").Generate(); +Console.WriteLine(response); ``` -This results in a URL like - -```plaintext -https://ik.imagekit.io/your_imagekit_id/default_image.jpg?tr=w-400%2Ch-300%2Car-4-3%2Cq-40%2Cc-force%2Ccm-extract%2Cfo-left%2Cf-jpeg%2Cl-text%2Ci-Imagekit%2Cfs-50%2Cl-end -``` +## Client configuration -**2\. Using full image URL** +Configure the client using environment variables: -This method allows you to add transformation parameters to an existing, complete URL that is already mapped to ImageKit using the `src` parameter. This method should be used if you have the complete URL mapped to ImageKit stored in your database. +```csharp +using Imagekit; -```cs -string imageURL = imagekit.Url(new Transformation().Width(400).Height(300)) - .Src("https://ik.imagekit.io/your_imagekit_id/endpoint/default_image.jpg") - .Generate(); +// Configured using the IMAGEKIT_PRIVATE_KEY, IMAGEKIT_WEBHOOK_SECRET and IMAGE_KIT_BASE_URL environment variables +ImageKitClient client = new(); ``` -This results in a URL like +Or manually: -```plaintext -https://ik.imagekit.io/your_imagekit_id/endpoint/default_image.jpg?tr=h-300,w-400 -``` +```csharp +using Imagekit; -The `.Url()` method accepts the following parameters. - -| Option | Description | -| --- | --- | -| urlEndpoint | Optional. The base URL has to be appended before the path of the image. If not specified, the URL Endpoint specified at the time of SDK initialization is used. For example, `https://ik.imagekit.io/your_imagekit_id/endpoint/` | -| path | Conditional. This is the path on which the image exists. For example, `/path/to/image.jpg`. Either the `path` or `src` parameter needs to be specified for URL generation. | -| src | Conditional. This is the complete URL of an image already mapped to ImageKit. For example, `https://ik.imagekit.io/your_imagekit_id/endpoint/path/to/image.jpg`. Either the `path` or `src` parameter needs to be specified for URL generation. | -| transformation | Optional. An array of objects specifying the transformation to be applied in the URL. The transformation name and the value should be specified as a key-value pair in the object. Different steps of a [chained transformation](https://docs.imagekit.io/features/image-transformations/chained-transformations) can be specified as the array's different objects. The complete list of supported transformations in the SDK and some examples of using them are given later. If you use a transformation name that is not specified in the SDK, it gets applied as it is in the URL. | -| transformationPosition | Optional. The default value is `path` that places the transformation string as a URL path parameter. It can also be specified as `query`, which adds the transformation string as the URL's query parameter `tr`. If you use the `src` parameter to create the URL, then the transformation string is always added as a query parameter. | -| queryParameters | Optional. These are the other query parameters that you want to add to the final URL. These can be any query parameters and not necessarily related to ImageKit. Especially useful if you want to add some versioning parameters to your URLs. | -| signed | Optional. Boolean. Default is `false`. If set to `false`, the SDK generates a signed image URL by adding the image signature to the image URL. This can only be used if you create the URL with the `urlEndpoint` and `path` parameters, not with the `src` parameter. | -| expireSeconds | Optional. Integer. It is meant to be used along with the `signed` parameter to specify the time in seconds from now when the URL should expire. If specified, the URL contains the expiry timestamp in the URL, and the image signature is modified accordingly. | - -#### Examples of generating URLs - -**1\. Chained Transformations as a query parameter** - -```cs -Transformation transformation = new Transformation() - .Width(400).Height(300) - .Chain() - .Rotation(90); - -string imageURL = imagekit.Url(transformation) - .Path("/default_image.jpg") - .UrlEndpoint("https://ik.imagekit.io/your_imagekit_id/endpoint") - .TransformationPosition("query") - .Generate(); +ImageKitClient client = new() +{ + PrivateKey = "My Private Key", +}; ``` -```plaintext -https://ik.imagekit.io/your_imagekit_id/endpoint/default_image.jpg?tr=h-300,w-400:rt-90 -``` +Or using a combination of the two approaches. -**2\. Sharpening, contrast adjustment, shadow and gradient transformations, along with a progressive JPEG format image.** +See this table for the available options: -There are some transforms like [Sharpening](https://docs.imagekit.io/features/image-transformations/image-enhancement-and-color-manipulation) that can be added to the URL with or without any other value. +| Property | Environment variable | Required | Default value | +| --------------- | -------------------------------- | -------- | --------------------------- | +| `PrivateKey` | `IMAGEKIT_PRIVATE_KEY` | true | - | +| `WebhookSecret` | `IMAGEKIT_WEBHOOK_SECRET` | false | - | +| `BaseUrl` | `IMAGE_KIT_BASE_URL` | true | `"https://api.imagekit.io"` | -```cs -string src = "https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg"; +### Modifying configuration -Transformation trans = new Transformation() - .Format("jpg") - .Progressive(false) - .EffectSharpen() - .EffectContrast(1) - .EffectShadow() - .EffectGradient(); +To temporarily use a modified client configuration, while reusing the same connection and thread pools, call `WithOptions` on any client or service: -string imageURL = imagekit.Url(trans) - .Src(src) - .Generate(); -``` +```csharp +using System; -**Note**: Because `src` parameter was used, the transformation string gets added as a query parameter `tr`. +var response = await client + .WithOptions(options => + options with + { + BaseUrl = "https://example.com", + Timeout = TimeSpan.FromSeconds(42), + } + ) + .Files.Upload(parameters); -```plaintext -https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg?tr=f-jpg%2Cpr-false%2Ce-sharpen%2Ce-contrast-1%2Ce-shadow%2Ce-gradient +Console.WriteLine(response); ``` -**3\. Signed URL that expires in 600 seconds with the default URL endpoint and other query parameters** - -```cs -Transformation trans = new Transformation() - .Height(300).Width(400); -string[] queryParams = { "v = 123" }; - -string imageURL = imagekit.Url(trans) - .Path("/default-image.jpg") - .QueryParameters(queryParams) - .Signed(false).ExpireSeconds(600) - .Generate(); -``` +Using a [`with` expression](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/with-expression) makes it easy to construct the modified options. -```plaintext -https://ik.imagekit.io/your_imagekit_id/tr:h-300,w-400/default-image.jpg?v=123&ik-t=1567358667&ik-s=f2c7cdacbe7707b71a83d49cf1c6110e3d701054 -``` +The `WithOptions` method does not affect the original client or service. -**4. Adding overlays** +## Requests and responses -ImageKit.io enables you to apply overlays to [images](https://docs.imagekit.io/features/image-transformations/overlay-using-layers) and [videos](https://docs.imagekit.io/features/video-transformation/overlay) using the raw parameter with the concept of [layers](https://docs.imagekit.io/features/image-transformations/overlay-using-layers#layers). The raw parameter facilitates incorporating transformations directly in the URL. A layer is a distinct type of transformation that allows you to define an asset to serve as an overlay, along with its positioning and additional transformations. +To send a request to the Image Kit API, build an instance of some `Params` class and pass it to the corresponding client method. When the response is received, it will be deserialized into an instance of a C# class. -**Text as overlays** +For example, `client.Files.Upload` should be called with an instance of `FileUploadParams`, and it will return an instance of `Task`. -You can add any text string over a base video or image using a text layer (l-text). +## Raw responses -For example: +The SDK defines methods that deserialize responses into instances of C# classes. However, these methods don't provide access to the response headers, status code, or the raw response body. -```cs -string path = "/default_image.jpg"; -Transformation trans = new Transformation() -.Width(400) -.Height(300) -.Raw("l-text,i-Imagekit,fs-50,l-end"); +To access this data, prefix any HTTP method call on a client or service with `WithRawResponse`: -string imageURL = imagekit.Url(trans).Path(path).TransformationPosition("query").Generate(); -``` -**Sample Result URL** +```csharp +var response = await client.WithRawResponse.Files.Upload(parameters); +var statusCode = response.StatusCode; +var headers = response.Headers; ``` -https://ik.imagekit.io/your_imagekit_id/default-image.jpg?tr=h-300,w-400,l-text,i-Imagekit,fs-50,l-end -``` - -**Image as overlays** -You can add an image over a base video or image using an image layer (l-image). +The raw `HttpResponseMessage` can also be accessed through the `RawMessage` property. -For example: +For non-streaming responses, you can deserialize the response into an instance of a C# class if needed: -```cs -string path = "/default_image.jpg"; -Transformation trans = new Transformation() -.Width(400) -.Height(300) -.Raw("l-image,i-default-image.jpg,w-100,b-10_CDDC39,l-end"); +```csharp +using System; +using Imagekit.Models.Files; -string imageURL = imagekit.Url(trans).Path(path).TransformationPosition("query").Generate(); -``` -**Sample Result URL** +var response = await client.WithRawResponse.Files.Upload(parameters); +FileUploadResponse deserialized = await response.Deserialize(); +Console.WriteLine(deserialized); ``` -https://ik.imagekit.io/your_imagekit_id/default-image.jpg?tr=h-300,w-400,l-image,i-default-image.jpg,w-100,b-10_CDDC39,l-end -``` - -**Solid color blocks as overlays** - -You can add solid color blocks over a base video or image using an image layer (l-image). -For example: +## URL generation -```cs -string path = "/img/sample-video.mp4"; -Transformation trans = new Transformation() -.Width(400) -.Height(300) -.Raw("l-image,i-ik_canvas,bg-FF0000,w-300,h-100,l-end"); +The ImageKit SDK provides a powerful `Helper.BuildUrl()` method for generating optimized image and video URLs with transformations. Here are examples ranging from simple URLs to complex transformations with overlays and signed URLs. -string imageURL = imagekit.Url(trans).Path(path).TransformationPosition("query").Generate(); -``` -**Sample Result URL** -``` -https://ik.imagekit.io/your_imagekit_id/img/sample-video.mp4?tr=h-300,w-400,l-image,i-ik_canvas,bg-FF0000,w-300,h-100,l-end -``` +### Basic URL generation -#### List of supported transformations - -The complete list of transformations supported and their usage in ImageKit can be found [here](https://docs.imagekit.io/features/image-transformations). The SDK gives a name to each transformation parameter, making the code simpler and more readable. If a transformation is supported in ImageKit, but a name for it cannot be found in the table below, then use the transformation code from ImageKit docs as the name when using the `url` function. - -| Supported Transformation Name | Translates to parameters | -| --- | --- | -| Height | h | -| Width | w | -| AspectRatio | ar | -| Quality | q | -| Crop | c | -| CropMode | cm | -| X | x | -| Y | y | -| Focus | fo | -| Format | f | -| Radius | r | -| Background | bg | -| Border | b | -| Rotation | rt | -| Blur | bl | -| Named | n | -| Progressive | pr | -| Lossless | lo | -| Trim | t | -| Metadata | md | -| ColorProfile | cp | -| DefaultImage | di | -| Dpr | dpr | -| EffectSharpen | e-sharpen | -| EffectUSM | e-usm | -| EffectContrast | e-contrast | -| EffectGray | e-grayscale | -| EffectShadow | e-shadow | -| EffectGradient | e-gradient | -| Original | orig | -| Raw | `replaced by the parameter value` | - -### File Upload - -The SDK provides a simple interface using the `.upload()` method to upload files to the ImageKit Media Library. It accepts all the parameters supported by the [ImageKit Upload API](https://docs.imagekit.io/api-reference/upload-file-api/server-side-file-upload). - -The `upload()` method requires at least the `file` and the `fileName` parameter to upload a file. You can pass other parameters supported by the ImageKit upload API using the same parameter name as specified in the upload API documentation. For example, to specify tags for a file at the time of upload, use the `tags` parameter as specified in the [documentation here](https://docs.imagekit.io/api-reference/upload-file-api/server-side-file-upload). - -Sample usage - -```cs - // Upload By URI -FileCreateRequest request = new FileCreateRequest -{ - file = "http://www.google.com/images/logos/ps_logo2.png", - fileName = "file_name.jpg", -}; -Result resp1 = imagekit.Upload(request); +Generate a simple URL without any transformations: -// Upload by bytes -byte[] bytes = System.IO.File.ReadAllBytes(@"image file path"); +```csharp +using Imagekit; +using Imagekit.Models; -FileCreateRequest ob = new FileCreateRequest +ImageKitClient client = new() { - file = bytes, - fileName = Guid.NewGuid().ToString() + PrivateKey = "private_key_xxx", }; -List tags = new List -{ - "Software", - "Developer", - "Engineer" -}; -ob.tags = tags; -string customCoordinates = "10,10,20,20"; -ob.customCoordinates = customCoordinates; -List responseFields = new List -{ - "isPrivateFile", - "tags", - "customCoordinates" -}; -ob.responseFields = responseFields; -List ext = new List(); -BackGroundImage bck1 = new BackGroundImage -{ - name = "remove-bg", - options = new options() - { add_shadow = true, semitransparency = false, bg_image_url = "http://www.google.com/images/logos/ps_logo2.png" } -}; -AutoTags autoTags = new AutoTags -{ - name = "google-auto-tagging", - maxTags = 5, - minConfidence = 95 -}; -ext.Add(bck1); -ext.Add(autoTags); -TransformationObject transformationObject = new TransformationObject -{ - value = "w-100" -}; -List postTransformations = new List(); -postTransformations.Add(transformationObject); -UploadTransformation uploadTransformation = new UploadTransformation -{ - pre = "l-text,i-Imagekit,fs-50,l-end", - post = postTransformations, -}; -ob.extensions = ext; -ob.webhookUrl = "https://webhook.site/c78d617f_33bc_40d9_9e61_608999721e2e"; -ob.useUniqueFileName = true; -ob.folder = "dummy_folder"; -ob.isPrivateFile = false; -ob.overwriteFile = true; -ob.overwriteAITags = true; -ob.overwriteTags = true; -ob.overwriteCustomMetadata = true; -ob.checks = "'file.size' < '1mb'" // To run server side checks before uploading files. Notice the quotes around file.size and 1mb. -ob.isPublished = true; -Result resp2 = imagekit.Upload(ob); - -//Get Base64 -byte[] imageArray = System.IO.File.ReadAllBytes(@"image file path"); -string base64ImageRepresentation = Convert.ToBase64String(imageArray); -// Upload by Base64 -FileCreateRequest ob2 = new FileCreateRequest +string url = client.Helper.BuildUrl(new SrcOptions { - file=base64ImageRepresentation, - fileName = Guid.NewGuid().ToString() -}; -Result resp = imagekit.Upload(ob2); + UrlEndpoint = "https://ik.imagekit.io/your_imagekit_id", + Src = "/path/to/image.jpg", +}); +Console.WriteLine(url); +// Result: https://ik.imagekit.io/your_imagekit_id/path/to/image.jpg ``` -### File Management - -The SDK provides a simple interface for all the [media APIs mentioned here](https://docs.imagekit.io/api-reference/media-afile-uploadpi) to manage your files. - -**1 . List & Search Files** +### URL generation with transformations -Accepts an object specifying the parameters to be used to list and search files. All parameters specified in the [documentation here](https://docs.imagekit.io/api-reference/media-api/list-and-search-files) can be passed as it is with the correct values to get the results. +Apply common transformations like resizing, cropping, and format conversion: -#### Applying Filters -Filter out the files by specifying the parameters. - -```cs -GetFileListRequest model = new GetFileListRequest -{ - Name = "file_name.jpg", - Type = "file", - Limit = 10, - Skip = 0, - Sort = "ASC_CREATED", - FileType = "image", - Tags = new string[] { "tag1", "tag2" } -}; -ResultList res = await imagekit.GetFileListRequestAsync(model); -``` - -#### Advance Search -In addition, you can fine-tune your query by specifying various filters by generating a query string in a Lucene-like syntax and providing this generated string as the value of the `SearchQuery`. +```csharp +using Imagekit; +using Imagekit.Models; -```cs -GetFileListRequest model = new GetFileListRequest +string url = client.Helper.BuildUrl(new SrcOptions { - SearchQuery = "createdAt >= '2d' OR size < '2mb' OR format='png'", -}; -ResultList res = await imagekit.GetFileListRequestAsync(model); -``` - -Detailed documentation can be found here for [advance search queries](https://docs.imagekit.io/api-reference/media-api/list-and-search-files#advanced-search-queries). - -**2\. Get File Details** - -Accepts the file ID and fetches the details as per the [API documentation here](https://docs.imagekit.io/api-reference/media-api/get-file-details). - -```cs -Result resp = await imagekit.GetFileDetail(file_Id); + UrlEndpoint = "https://ik.imagekit.io/your_imagekit_id", + Src = "/path/to/image.jpg", + Transformation = + [ + new Transformation + { + Width = 400, + Height = 300, + Crop = Crop.MaintainRatio, + Quality = 80, + Format = Format.Webp, + }, + ], +}); +Console.WriteLine(url); +// Result: https://ik.imagekit.io/your_imagekit_id/path/to/image.jpg?tr=w-400,h-300,q-80,c-maintain_ratio,f-webp ``` -**3\. File Update** +### URL generation with image overlay -Accepts an object of class `FileUpdateRequest` specifying the parameters to be used to update file details. All parameters specified in the \[documentation here\] (https://docs.imagekit.io/api-reference/media-api/update-file-details) can be passed via their setter functions to get the results. +Add image overlays to your base image: -```cs -FileUpdateRequest updateob = new FileUpdateRequest -{ - fileId = "fileId", -}; -List updatetags = new List -{ - "Software", - "Developer", - "Engineer" -}; -updateob.tags = updatetags; -string updatecustomCoordinates = "10,10,20,20"; -updateob.customCoordinates = updatecustomCoordinates; -List updateresponseFields = new List -{ - "isPrivateFile", - "tags", - "customCoordinates" -}; +```csharp +using Imagekit; +using Imagekit.Models; + +string url = client.Helper.BuildUrl(new SrcOptions +{ + UrlEndpoint = "https://ik.imagekit.io/your_imagekit_id", + Src = "/path/to/base-image.jpg", + Transformation = + [ + new Transformation + { + Width = 500, + Height = 400, + Overlay = new ImageOverlay("/path/to/overlay-logo.png") + { + Position = new OverlayPosition { X = "10", Y = "10" }, + Transformation = [new Transformation { Width = 100, Height = 50 }], + }, + }, + ], +}); +Console.WriteLine(url); +// Result: URL with image overlay positioned at x:10, y:10 +``` + +### URL generation with text overlay + +Add customized text overlays: + +```csharp +using Imagekit; +using Imagekit.Models; + +string url = client.Helper.BuildUrl(new SrcOptions +{ + UrlEndpoint = "https://ik.imagekit.io/your_imagekit_id", + Src = "/path/to/base-image.jpg", + Transformation = + [ + new Transformation + { + Width = 600, + Height = 400, + Overlay = new TextOverlay("Sample Text Overlay") + { + Position = new OverlayPosition { X = "50", Y = "50", Focus = Focus.Center }, + Transformation = + [ + new TextOverlayTransformation + { + FontSize = 40, + FontFamily = "Arial", + FontColor = "FFFFFF", + Typography = "b", // bold + }, + ], + }, + }, + ], +}); +Console.WriteLine(url); +// Result: URL with bold white Arial text overlay at center position +``` + +### URL generation with multiple overlays + +Combine multiple overlays for complex compositions: + +```csharp +using Imagekit; +using Imagekit.Models; + +string url = client.Helper.BuildUrl(new SrcOptions +{ + UrlEndpoint = "https://ik.imagekit.io/your_imagekit_id", + Src = "/path/to/base-image.jpg", + Transformation = + [ + new Transformation + { + Width = 800, + Height = 600, + Overlay = new TextOverlay("Header Text") + { + Position = new OverlayPosition { X = "20", Y = "20" }, + Transformation = + [ + new TextOverlayTransformation { FontSize = 30, FontColor = "000000" }, + ], + }, + }, + new Transformation + { + Overlay = new ImageOverlay("/watermark.png") + { + Position = new OverlayPosition { Focus = Focus.BottomRight }, + Transformation = [new Transformation { Width = 100, Opacity = 70 }], + }, + }, + ], +}); +Console.WriteLine(url); +// Result: URL with text overlay at top-left and semi-transparent watermark at bottom-right +``` + +### Signed URLs for secure delivery + +Generate signed URLs that expire after a specified time for secure content delivery: + +```csharp +using Imagekit; +using Imagekit.Models; -List extModel = new List(); -BackGroundImage bck = new BackGroundImage +// Generate a signed URL that expires in 1 hour (3600 seconds) +string url = client.Helper.BuildUrl(new SrcOptions { - name = "remove-bg", - options = new options() { add_shadow = true, semitransparency = false, bg_color = "green" } -}; -extModel.Add(bck); -updateob.extensions = extModel; -updateob.webhookUrl = "https://webhook.site/c78d617f_33bc_40d9_9e61_608999721e2e"; -Result updateresp = imagekit.UpdateFileDetail(updateob); -``` + UrlEndpoint = "https://ik.imagekit.io/your_imagekit_id", + Src = "/private/secure-image.jpg", + Transformation = [new Transformation { Width = 400, Height = 300, Quality = 90 }], + Signed = true, + ExpiresIn = 3600, // URL expires in 1 hour +}); +Console.WriteLine(url); +// Result: URL with signature parameters (?ik-t=timestamp&ik-s=signature) -**Update publish status** - -If `publish` is included in the update options, no other parameters are allowed. - -```cs -FileUpdateRequest updateob = new FileUpdateRequest -{ - fileId = "fileId", -}; -PublishStatus publishStatus = new PublishStatus +// Generate a signed URL that doesn't expire +string permanentSignedUrl = client.Helper.BuildUrl(new SrcOptions { - isPublished = false -}; -updateob.publish = publishStatus; -Result updateresp = imagekit.UpdateFileDetail(updateob); -``` - -**4\. Delete File** - -Accept the file ID and delete a file as per the [API documentation here](https://docs.imageKit.io/api-reference/media-api/delete-file). - -```cs -String fileId = "file_Id"; -ResultDelete result = imageKit.deleteFile(fileId); + UrlEndpoint = "https://ik.imagekit.io/your_imagekit_id", + Src = "/private/secure-image.jpg", + Signed = true, + // No ExpiresIn means the URL won't expire +}); +Console.WriteLine(permanentSignedUrl); +// Result: URL with signature parameter (?ik-s=signature) ``` -**5\. Delete files (bulk)** - -Accepts the file IDs to delete files as per the [API documentation here](https://docs.imageKit.io/api-reference/media-api/delete-files-bulk). - -```cs -List ob3 = new List(); -ob3.Add("fileId_1"); -ob3.Add("fileId_2"); -ResultFileDelete resultFileDelete = imagekit.BulkDeleteFiles(ob3); -``` +### Using Raw transformations for undocumented features -**6\. Copy file** +ImageKit frequently adds new transformation parameters that might not yet be documented in the SDK. You can use the `Raw` parameter to access these features or create custom transformation strings: -Accepts an object of class `CopyFileRequest` specifying the parameters to be used to copy a file. All parameters specified in the [documentation here](https://docs.imageKit.io/api-reference/media-api/copy-file) can be passed via their setter functions to get the results. +```csharp +using Imagekit; +using Imagekit.Models; -```cs -CopyFileRequest cpyRequest = new CopyFileRequest +string url = client.Helper.BuildUrl(new SrcOptions { - sourceFilePath = "path_1", - destinationPath = "path_2", - includeFileVersions = true -}; -ResultNoContent resultNoContent = imagekit.CopyFile(cpyRequest); + UrlEndpoint = "https://ik.imagekit.io/your_imagekit_id", + Src = "/path/to/image.jpg", + Transformation = + [ + new Transformation { Width = 400, Height = 300 }, + new Transformation { Raw = "something-new" }, + ], +}); +Console.WriteLine(url); +// Result: https://ik.imagekit.io/your_imagekit_id/path/to/image.jpg?tr=w-400,h-300:something-new ``` -**7\. Move file** - -Accepts an object of class `MoveFileRequest` specifying the parameters to be used to move a file. All parameters specified in the [documentation here](https://docs.imageKit.io/api-reference/media-api/move-file) can be passed via their setter functions to get the results. - -```cs -MoveFileRequest moveFile = new MoveFileRequest -{ - sourceFilePath = "path_1", - destinationPath = "path_2" -}; -ResultNoContent resultNoContentMoveFile = imagekit.MoveFile(moveFile); -``` +## Authentication parameters for client-side uploads -**8\. Rename file** +Generate authentication parameters for secure client-side file uploads: -Accepts an object of class `RenameFileRequest` specifying the parameters to be used to rename a file. All parameters specified in the [documentation here](https://docs.imageKit.io/api-reference/media-api/rename-file) can be passed via their setter functions to get the results. +```csharp +using Imagekit; -```cs -RenameFileRequest renameFileRequest = new RenameFileRequest +ImageKitClient client = new() { - filePath = "path_1", - newFileName = "file_name", - purgeCache = false + PrivateKey = "private_key_xxx", }; -ResultRenameFile resultRenameFile = imagekit.RenameFile(renameFileRequest); -``` - -### Tags Management - -The SDK provides a simple interface to manage your tags. - -**9\. Add tags** -Accepts an object of class `TagsRequest` specifying the parameters to be used to add tags. All parameters specified in the [documentation here](https://docs.imageKit.io/api-reference/media-api/add-tags-bulk) can be passed via their setter functions to get the results. +// Generate authentication parameters for client-side uploads +var authParams = client.Helper.GetAuthenticationParameters(); +Console.WriteLine(authParams); +// Result: AuthenticationParameters { Token = "", Expire = , Signature = "" } -```cs -TagsRequest tagsRequest = new TagsRequest -{ - tags = new List - { - "tag_1", - "tag_2" - }, - fileIds = new List - { - "fileId_1", - }, -}; -ResultTags resultTags = imagekit.AddTags(tagsRequest); +// Generate with custom token and expiry (seconds from now) +var customAuthParams = client.Helper.GetAuthenticationParameters("my-custom-token", 1800); +Console.WriteLine(customAuthParams); +// Result: AuthenticationParameters { Token = "my-custom-token", Expire = 1800, Signature = "" } ``` -**10\. Remove tags** - -Accepts an object of class `TagsRequest` specifying the parameters to be used to remove tags. All parameters specified in the [documentation here](https://docs.imageKit.io/api-reference/media-api/remove-tags-bulk) can be passed via their setter functions to get the results. - -```cs -TagsRequest removeTagsRequest = new TagsRequest -{ - tags = new List - { - "tag_1", - "tag_2" - }, - fileIds = new List - { - "fileId_1", - }, -}; -ResultTags removeTags = imagekit.RemoveTags(removeTagsRequest); -``` +These authentication parameters can be used in client-side upload forms to securely upload files without exposing your private API key. -**11\. Remove AI tags** +## Webhook verification -Accepts an object of class `AITagsRequest` specifying the parameters to be used to remove AI tags. All parameters specified in the [documentation here](https://docs.imageKit.io/api-reference/media-api/remove-aitags-bulk) can be passed via their setter functions to get the results. +For detailed information about webhook setup, signature verification, and handling different webhook events, refer to the [ImageKit webhook documentation](https://imagekit.io/docs/webhooks). -```cs -AITagsRequest removeAITagsRequest = new AITagsRequest -{ - AITags = new List - { - "tag_1", - "tag_2" - }, - fileIds = new List - { - "fileId_1", - } -}; -ResultTags removeAITags = imagekit.RemoveAITags(removeAITagsRequest); -``` +## Error handling -**12\. Get File Versions** +The SDK throws custom unchecked exception types: -Accepts the file ID and fetches the details as per the [API documentation here](https://docs.imagekit.io/api-reference/media-api/get-file-versions). +- `ImageKitApiException`: Base class for API errors. See this table for which exception subclass is thrown for each HTTP status code: -```cs -String fileId = "file_id_1"; -ResultFileVersions resultFileVersions = imageKit.getFileVersions(fileId); -``` +| Status | Exception | +| ------ | --------------------------------------- | +| 400 | `ImageKitBadRequestException` | +| 401 | `ImageKitUnauthorizedException` | +| 403 | `ImageKitForbiddenException` | +| 404 | `ImageKitNotFoundException` | +| 422 | `ImageKitUnprocessableEntityException` | +| 429 | `ImageKitRateLimitException` | +| 5xx | `ImageKit5xxException` | +| others | `ImageKitUnexpectedStatusCodeException` | -**13\. Get File Version details** +Additionally, all 4xx errors inherit from `ImageKit4xxException`. -Accepts the file ID and version ID and fetches the details as per the [API documentation here](https://docs.imagekit.io/api-reference/media-api/get-file-version-details). +- `ImageKitIOException`: I/O networking errors. -```cs -String fileId = "file_Id"; -String versionId = "version_Id"; -ResultFileVersionDetails resultFileVersionDetails = imageKit.getFileVersionDetails(fileId, versionId); -``` +- `ImageKitInvalidDataException`: Failure to interpret successfully parsed data. For example, when accessing a property that's supposed to be required, but the API unexpectedly omitted it from the response. -**14\. Delete FileVersion** +- `ImageKitException`: Base class for all exceptions. -Accepts an object of class `DeleteFileVersionRequest` specifying the parameters to be used to delete the file version. All parameters specified in the [documentation here](https://docs.imageKit.io/api-reference/media-api/delete-file-version) can be passed via their setter functions to get the results. +## Network options -```cs -DeleteFileVersionRequest delRequest = new DeleteFileVersionRequest -{ - fileId = "file_Id", - versionId = "version_Id" -}; -ResultNoContent resultNoContent1 = imagekit.DeleteFileVersion(delRequest); -``` +### Retries -**15\. Restore file Version** +The SDK automatically retries 2 times by default, with a short exponential backoff between requests. -Accepts the fileId and versionId to restore the file version as per the [API documentation here](https://docs.imageKit.io/api-reference/media-api/restore-file-version). +Only the following error types are retried: -```cs -Result result = imagekit.RestoreFileVersion("file_Id", "version_Id"); -``` +- Connection errors (for example, due to a network connectivity problem) +- 408 Request Timeout +- 409 Conflict +- 429 Rate Limit +- 5xx Internal -### Folder Management +The API may also explicitly instruct the SDK to retry or not retry a request. -**16\. Create Folder** +To set a custom number of retries, configure the client using the `MaxRetries` method: -Accepts an object of class `CreateFolderRequest` specifying the parameters to be used to create a folder. All parameters specified in the [documentation here](https://docs.imageKit.io/api-reference/media-api/create-folder) can be passed via their setter functions to get the results. +```csharp +using Imagekit; -```cs -CreateFolderRequest createFolderRequest = new CreateFolderRequest -{ - folderName = "folder_name", - parentFolderPath = "source/folder/path" -}; -ResultEmptyBlock resultEmptyBlock = imagekit.CreateFolder(createFolderRequest); +ImageKitClient client = new() { MaxRetries = 3 }; ``` -**17\. Copy Folder** +Or configure a single method call using [`WithOptions`](#modifying-configuration): -Accepts an object of class `CopyFolderRequest` specifying the parameters to be used to copy a folder. All parameters specified in the [documentation here](https://docs.imageKit.io/api-reference/media-api/copy-folder) can be passed via their setter functions to get the results. +```csharp +using System; -```cs -CopyFolderRequest cpyFolderRequest = new CopyFolderRequest -{ - sourceFolderPath = "path_1", - destinationPath = "path_2", - includeFileVersions = true -}; +var response = await client + .WithOptions(options => + options with { MaxRetries = 3 } + ) + .Files.Upload(parameters); -ResultOfFolderActions resultOfFolderActions = imagekit.CopyFolder(cpyFolderRequest); +Console.WriteLine(response); ``` -**18\. Move Folder** - -Accepts an object of class `MoveFolderRequest` specifying the parameters to be used to move a folder. All parameters specified in the [documentation here](https://docs.imageKit.io/api-reference/media-api/move-folder) can be passed via their setter functions to get the results. - -```cs -MoveFolderRequest moveFolderRequest = new MoveFolderRequest -{ - sourceFolderPath="path_1", - destinationPath="path_2" -}; +### Timeouts -ResultOfFolderActions resultOfFolderActions1 = imagekit.MoveFolder(moveFolderRequest); -``` +Requests time out after 1 minute by default. -**19\. Delete Folder** +To set a custom timeout, configure the client using the `Timeout` option: -Accepts an object of class `DeleteFolderRequest` specifying the parameters to be used to delete a folder. All parameters specified in the [documentation here](https://docs.imageKit.io/api-reference/media-api/delete-folder) can be passed via their setter functions to get the results. +```csharp +using System; +using Imagekit; -```cs -DeleteFolderRequest deleteFolderRequest = new DeleteFolderRequest -{ - folderPath="source/folder/path/folder_name", -}; -ResultNoContent resultNoContent2 = imagekit.DeleteFolder(deleteFolderRequest); +ImageKitClient client = new() { Timeout = TimeSpan.FromSeconds(42) }; ``` -### Job Management +Or configure a single method call using [`WithOptions`](#modifying-configuration): -**20\. Get Bulk Job Status** +```csharp +using System; -Accepts the jobId to get bulk job status as per the [API documentation here](https://docs.imageKit.io/api-reference/media-api/copy-move-folder-status). +var response = await client + .WithOptions(options => + options with { Timeout = TimeSpan.FromSeconds(42) } + ) + .Files.Upload(parameters); -```cs -String jobId = "job_Id"; -ResultBulkJobStatus resultBulkJobStatus = imageKit.getBulkJobStatus(jobId); +Console.WriteLine(response); ``` -### Purge - -**21\. Purge Cache** +### Proxies -Accepts a full URL of the file for which the cache has to be cleared as per the [API documentation here](https://docs.imageKit.io/api-reference/media-api/purge-cache). +To route requests through a proxy, configure your client with a custom [`HttpClient`](https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=net-10.0): -```cs -ResultCache result = imageKit.purgeCache("https://ik.imageKit.io/imagekit-id/default-image.jpg"); -``` - -**22\. Purge Cache Status** +```csharp +using System.Net; +using System.Net.Http; +using Imagekit; -Accepts a request ID and fetch purge cache status as per the [API documentation here](https://docs.imageKit.io/api-reference/media-api/purge-cache-status) +var httpClient = new HttpClient +( + new HttpClientHandler + { + Proxy = new WebProxy("https://example.com:8080") + } +); -```cs -String requestId = "cache_request_Id"; -ResultCacheStatus result = imageKit.getPurgeCacheStatus(requestId); +ImageKitClient client = new() { HttpClient = httpClient }; ``` -### Metadata - -Accepts the file ID and fetches the metadata as per the [API documentation here](https://docs.imageKit.io/api-reference/metadata-api/get-image-metadata-for-uploaded-media-files) - -**23\. Get File Metadata** - -```cs -String fileId = "file_id"; -ResultMetaData result = imageKit.getFileMetadata(fileId); -``` +## Undocumented API functionality -Another way to get metadata from a remote file URL as per the [API documentation here](https://docs.imageKit.io/api-reference/metadata-api/get-image-metadata-from-remote-url). This file should be accessible over the imageKit.io URL-endpoint. +The SDK is typed for convenient usage of the documented API. However, it also supports working with undocumented or not yet supported parts of the API. -```cs -String url = "Remote File URL"; -ResultMetaData result = imageKit.getRemoteFileMetadata(url); -``` +### Parameters -**24\. Create Custom MetaData Fields** +To set undocumented parameters, a constructor exists that accepts dictionaries for additional header, query, and body values. If the method type doesn't support request bodies (e.g. `GET` requests), the constructor will only accept a header and query dictionary. -Accepts an object of class `CustomMetaDataFieldCreateRequest` specifying the parameters to be used to create cusomMetaDataFields. All parameters specified in the [documentation here](https://docs.imageKit.io/api-reference/custom-metadata-fields-api/create-custom-metadata-field) can be passed as-is with the correct values to get the results. +```csharp +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Files; -Check for the [Allowed Values In The Schema](https://docs.imageKit.io/api-reference/custom-metadata-fields-api/create-custom-metadata-field#allowed-values-in-the-schema-object). +FileUploadParams parameters = new +( + rawHeaderData: new Dictionary() + { + { "Custom-Header", JsonSerializer.SerializeToElement(42) } + }, -#### Examples: + rawQueryData: new Dictionary() + { + { "custom_query_param", JsonSerializer.SerializeToElement(42) } + }, -```cs -CustomMetaDataFieldCreateRequest requestModel = new CustomMetaDataFieldCreateRequest -{ - name = "custom-meta-1", - label = "Testmeta" -}; -CustomMetaDataFieldSchemaObject schema = new CustomMetaDataFieldSchemaObject + rawBodyData: new Dictionary() + { + { "custom_body_param", JsonSerializer.SerializeToElement(42) } + } +) { - type = "Number", - minValue = 2000, - maxValue = 3000, - isValueRequired = true, - defaultValue = "2500" + // Documented properties can still be added here. + // In case of conflict, these parameters take precedence over the custom parameters. + Expire = 0 }; - -requestModel.schema = schema; -ResultCustomMetaDataField resultCustomMetaDataField1 = imagekit.CreateCustomMetaDataFields(requestModel); ``` -* Date type Example: +The raw parameters can also be accessed through the `RawHeaderData`, `RawQueryData`, and `RawBodyData` (if available) properties. -```cs -CustomMetaDataFieldCreateRequest requestModelDate = new CustomMetaDataFieldCreateRequest -{ - name = "custom_meta_Date", - label = "TestmetaDate" -}; -CustomMetaDataFieldSchemaObject schemaDate = new CustomMetaDataFieldSchemaObject -{ - type = "Date", - minValue = "2022-11-30T10:11:10+00:00", - maxValue = "2022-12-30T10:11:10+00:00" -}; -requestModelDate.schema = schemaDate; -ResultCustomMetaDataField resultCustomMetaDataFieldDate = imagekit.CreateCustomMetaDataFields(requestModelDate); -``` +This can also be used to set a documented parameter to an undocumented or not yet supported _value_, as long as the parameter is optional. If the parameter is required, omitting its `init` property will result in a compile-time error. To work around this, the `FromRawUnchecked` method can be used: -**25\. Get CustomMetaDataFields** +```csharp +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Models.Files; -Accepts the includeDeleted boolean and fetches the metadata as per the [API documentation here](https://docs.imageKit.io/api-reference/custom-metadata-fields-api/get-custom-metadata-field) +var parameters = FileUploadParams.FromRawUnchecked +( -```cs -bool includeDeleted = true; -ResultCustomMetaDataFieldList resultCustomMetaDataFieldList = imageKit.getCustomMetaDataFields(includeDeleted); + rawHeaderData: new Dictionary(), + rawQueryData: new Dictionary(), + rawBodyData: new Dictionary + { + { + "file", + JsonSerializer.SerializeToElement("custom value") + } + } +); ``` -**26\. Edit Custom MetaData Fields** +### Nested Parameters -Accepts an ID of customMetaDataField and an object of class `CustomMetaDataFieldUpdateRequest` specifying the parameters to be used to edit cusomMetaDataFields as per the [API documentation here](https://docs.imageKit.io/api-reference/custom-metadata-fields-api/update-custom-metadata-field). +Undocumented properties, or undocumented values of documented properties, on nested parameters can be set similarly, using a dictionary in the constructor of the nested parameter. -```cs -CustomMetaDataFieldUpdateRequest requestUpdateModel = new CustomMetaDataFieldUpdateRequest -{ - Id = "field_id", -}; -CustomMetaDataFieldSchemaObject updateschema = new CustomMetaDataFieldSchemaObject +```csharp +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Models.Files; + +FileUploadParams parameters = new() { - type = "Number", - minValue = 8000, - maxValue = 3000 + Transformation = new + ( + new Dictionary + { + { "custom_nested_param", JsonSerializer.SerializeToElement(42) } + } + ) }; - -requestUpdateModel.schema = updateschema; -ResultCustomMetaDataField resultCustomMetaDataFieldUpdate = imagekit.UpdateCustomMetaDataFields(requestUpdateModel); ``` -**27\. Delete Custom MetaData Fields** +Required properties on the nested parameter can also be changed or omitted using the `FromRawUnchecked` method: -Accepts the id to delete the customMetaDataFields as per the [API documentation here](https://docs.imageKit.io/api-reference/custom-metadata-fields-api/delete-custom-metadata-field). +```csharp +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Models.Files; -```cs -ResultNoContent resultNoContent = imageKit.DeleteCustomMetaDataField("field_id"); +FileUploadParams parameters = new() +{ + Transformation = Transformation.FromRawUnchecked + ( + new Dictionary + { + { "required_property", JsonSerializer.SerializeToElement("custom value") } + } + ) +}; ``` -## Utility functions - -We have included the following commonly used utility functions in this library. - -### Authentication Parameter Generation +### Response properties -If you are looking to implement client-side file upload, you will need a token, expiry timestamp, and a valid signature for that upload. The SDK provides a simple method that you can use in your code to generate these authentication parameters for you. +To access undocumented response properties, the `RawData` property can be used: -_Note: The Private API Key should never be exposed in any client-side code. You must always generate these authentication parameters on the server-side_ +```csharp +using System.Text.Json; -```cs -AuthParamResponse resp = imageKit.GetAuthenticationParameters(); -``` - -Returns - -```javascript +var response = await client.Files.Upload(parameters); +if (response.RawData.TryGetValue("my_custom_key", out JsonElement value)) { - "token" : "unique_token", - "expire" : "valid_expiry_timestamp", - "signature" : "generated_signature" + // Do something with `value` } ``` -Both the `token` and `expire` parameters are optional. If not specified, the SDK uses the [uuid](https://www.npmjs.com/package/uuid) package to generate a random token and also generates a valid expiry timestamp internally. The value of the `token` and `expire` used to generate the signature is always returned in the response, no matter if they are provided as an input to this method or not. - -### Distance calculation between two pHash values - -Perceptual hashing allows you to construct a hash value that uniquely identifies an input image based on an image's contents. [imageKit.io metadata API](https://docs.imageKit.io/api-reference/metadata-api) returns the pHash value of an image in the response. You can use this value to [find a duplicate (or similar) image](https://docs.imageKit.io/api-reference/metadata-api#using-phash-to-find-similar-or-duplicate-images) by calculating the distance between the two images' pHash value. - -This SDK exposes `PHashDistance` function to calculate the distance between two pHash values. It accepts two pHash hexadecimal strings and returns a numeric value indicative of the level of difference between the two images. +`RawData` is a `IReadonlyDictionary`. It holds the full data received from the API server. -```cs -public static int CalculateDistance() { - // asynchronously fetch metadata of two uploaded image files - // ... - // Extract pHash strings from both: say 'firstHash' and 'secondHash' - // ... - // Calculate the distance between them: - int distance = imageKit.PHashDistance(firstHash, secondHash); - return distance; -} -``` +### Response validation -#### Distance calculation examples +In rare cases, the API may return a response that doesn't match the expected type. For example, the SDK may expect a property to contain a `string`, but the API could return something else. -```cs -imageKit.PHashDistance('firstHash', 'secondHash'); -// output: 0 (same image) +By default, the SDK will not throw an exception in this case. It will throw `ImageKitInvalidDataException` only if you directly access the property. -imageKit.PHashDistance('firstHash', 'secondHash'); -// output: 17 (similar images) +If you would prefer to check that the response is completely well-typed upfront, then either call `Validate`: -imageKit.PHashDistance('firstHash', 'secondHash'); -// output: 37 (dissimilar images) +```csharp +var response = await client.Files.Upload(parameters); +response.Validate(); ``` -## Handling errors - -Catch and respond to invalid data, internal problems, and more. +Or configure the client using the `ResponseValidation` option: -Imagekit .Net SDK raises exceptions for many reasons, such as being not found, invalid parameters, authentication errors, and internal server errors. We recommend writing code that gracefully handles all possible API exceptions. - -#### Example: +```csharp +using Imagekit; -```cs -try -{ - // Use ImageKit's SDK to make requests... -} -catch (InvalidOperationException ex) -{ - Console.Write("Invalid operation. Please try again."); -} -catch (FormatException ex) -{ - Console.Write("Not a valid format. Please try again."); -} -catch (WebServiceException webEx) -{ - /* - webEx.StatusCode = 400 - webEx.StatusDescription = ArgumentNullException - webEx.ErrorCode = ArgumentNullException - webEx.ErrorMessage = Value cannot be null. Parameter name: Name - webEx.StackTrace = (your Server Exception StackTrace - in DebugMode) - webEx.ResponseDto = (your populated Response DTO) - webEx.ResponseStatus = (your populated Response Status DTO) - webEx.GetFieldErrors() = (individual errors for each field if any) - */ -} +ImageKitClient client = new() { ResponseValidation = true }; ``` -## Access request-id, other response headers and HTTP status code +Or configure a single method call using [`WithOptions`](#modifying-configuration): -You can access success or error object to access the HTTP status code and response headers. +```csharp +using System; -```cs -// Success -var response = await imagekit.PurgeStatus(requestId); -console.Write(response.statusCode); // 200 -// {'content-type': 'application/json', 'x-request-id': 'ee560df4-d44f-455e-a48e-29dfda49aec5'} -console.Write(response.Raw); -try -{ - await imagekit.PurgeStatus(requestId); -} -catch (Exception ex) -{ - console.Write(ex.Message); - // {'content-type': 'application/json', 'x-request-id': 'ee560df4-d44f-455e-a48e-29dfda49aec5'} -} -``` +var response = await client + .WithOptions(options => + options with { ResponseValidation = true } + ) + .Files.Upload(parameters); -## Support +Console.WriteLine(response); +``` -For any feedback or to report any issues or general implementation support, please reach out to [support@imageKit.io](mailto:support@imageKit.io) +## Semantic versioning -## Links +This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions: -* [Documentation](https://docs.imageKit.io) -* [Main website](https://imageKit.io) +1. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_ +2. Changes that we do not expect to impact the vast majority of users in practice. -## License +We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience. -This project is licensed under the MIT License - see the [LICENSE](LICENSE) File for details \ No newline at end of file +We are keen for your feedback; please open an [issue](https://www.github.com/imagekit-developer/imagekit-dotnet/issues) with questions, bugs, or suggestions. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..8e64327a --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,27 @@ +# Security Policy + +## Reporting Security Issues + +This SDK is generated by [Stainless Software Inc](http://stainless.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken. + +To report a security issue, please contact the Stainless team at security@stainless.com. + +## Responsible Disclosure + +We appreciate the efforts of security researchers and individuals who help us maintain the security of +SDKs we generate. If you believe you have found a security vulnerability, please adhere to responsible +disclosure practices by allowing us a reasonable amount of time to investigate and address the issue +before making any information public. + +## Reporting Non-SDK Related Security Issues + +If you encounter security issues that are not directly related to SDKs but pertain to the services +or products provided by Image Kit, please follow the respective company's security reporting guidelines. + +### Image Kit Terms and Policies + +Please contact developer@imagekit.io for any questions or concerns regarding the security of our services. + +--- + +Thank you for helping us keep the SDKs and systems they interact with secure. diff --git a/codecov.yml b/codecov.yml deleted file mode 100644 index 52dd98c0..00000000 --- a/codecov.yml +++ /dev/null @@ -1,15 +0,0 @@ -# Team Yaml -coverage: - round: down - precision: 5 - -# Repository Yaml -coverage: - round: up - range: 0..10 - -# Used in Codecov after updating -coverage: - round: up - range: 0..10 - precision: 5 diff --git a/examples/.keep b/examples/.keep new file mode 100644 index 00000000..e69de29b diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 00000000..59759611 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,71 @@ +{ + "packages": { + ".": {} + }, + "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json", + "include-v-in-tag": true, + "include-component-in-tag": false, + "versioning": "prerelease", + "prerelease": true, + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": false, + "pull-request-header": "Automated Release PR", + "pull-request-title-pattern": "release: ${version}", + "changelog-sections": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "perf", + "section": "Performance Improvements" + }, + { + "type": "revert", + "section": "Reverts" + }, + { + "type": "chore", + "section": "Chores" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "style", + "section": "Styles" + }, + { + "type": "refactor", + "section": "Refactors" + }, + { + "type": "test", + "section": "Tests", + "hidden": true + }, + { + "type": "build", + "section": "Build System" + }, + { + "type": "ci", + "section": "Continuous Integration", + "hidden": true + } + ], + "release-type": "simple", + "extra-files": [ + { + "type": "xml", + "path": "**/*.csproj", + "xpath": "//Project/PropertyGroup/VersionPrefix", + "glob": true + } + ] +} \ No newline at end of file diff --git a/scripts/bootstrap b/scripts/bootstrap new file mode 100755 index 00000000..2527407d --- /dev/null +++ b/scripts/bootstrap @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +echo "==> Running dotnet tool restore" +dotnet tool restore diff --git a/scripts/build b/scripts/build new file mode 100755 index 00000000..3ce1e776 --- /dev/null +++ b/scripts/build @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +echo "==> Running dotnet build" +dotnet build diff --git a/scripts/format b/scripts/format new file mode 100755 index 00000000..52057e25 --- /dev/null +++ b/scripts/format @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +echo "==> Running dotnet csharpier format" +dotnet csharpier format . diff --git a/scripts/lint b/scripts/lint new file mode 100755 index 00000000..8c2f26c7 --- /dev/null +++ b/scripts/lint @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +echo "==> Running dotnet csharpier check" +dotnet csharpier check . + +echo "==> Running dotnet format" +dotnet format style --severity info --verify-no-changes +dotnet format analyzers --severity info --verify-no-changes diff --git a/scripts/test b/scripts/test new file mode 100755 index 00000000..bd71d26e --- /dev/null +++ b/scripts/test @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -e + +cd -- "$(dirname -- "$0")/.." + + + +echo "==> Running tests" +dotnet test diff --git a/scripts/test-helper b/scripts/test-helper new file mode 100755 index 00000000..8d57f11e --- /dev/null +++ b/scripts/test-helper @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -e + +cd -- "$(dirname -- "$0")/.." + +echo "==> Running Helper tests" +dotnet test src/Imagekit.Tests/Imagekit.Tests.csproj --filter "FullyQualifiedName~Imagekit.Tests.Helper" -f net8.0 diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 00000000..37a85233 --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,34 @@ + + + Image Kit + Image Kit + sdk;api;http;library + Apache-2.0 + Copyright 2026 Image Kit + https://imagekit.io/docs/api-reference + https://www.github.com/imagekit-developer/imagekit-dotnet + git + + + true + + $(NoWarn),1573,1591 + + + true + true + true + embedded + + net8.0;netstandard2.0 + 12 + Debug;Release + true + true + enable + true + disable + + $(NoWarn),0618 + + \ No newline at end of file diff --git a/src/Imagekit.Tests/Core/JsonDictionaryTest.cs b/src/Imagekit.Tests/Core/JsonDictionaryTest.cs new file mode 100644 index 00000000..ad185457 --- /dev/null +++ b/src/Imagekit.Tests/Core/JsonDictionaryTest.cs @@ -0,0 +1,406 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; + +namespace Imagekit.Tests.Core; + +public class JsonDictionaryTest : TestBase +{ + [Fact] + public void DefaultConstructor_CreatesEmptyDictionary() + { + var dict = new JsonDictionary(); + + Assert.Empty(dict.Freeze()); + } + + [Fact] + public void DictionaryConstructor_CopiesData() + { + var source = new Dictionary + { + ["foo"] = JsonSerializer.SerializeToElement("bar"), + ["bar"] = JsonSerializer.SerializeToElement(42), + }; + + var dict = new JsonDictionary(source); + + var frozen = dict.Freeze(); + Assert.Equal(2, frozen.Count); + Assert.Equal("bar", frozen["foo"].GetString()); + Assert.Equal(42, frozen["bar"].GetInt32()); + } + + [Fact] + public void FrozenDictionaryConstructor_UsesProvidedDictionary() + { + var source = FrozenDictionary.ToFrozenDictionary( + new Dictionary + { + ["foo"] = JsonSerializer.SerializeToElement("bar"), + } + ); + + var dict = new JsonDictionary(source); + + var frozen = dict.Freeze(); + Assert.Same(source, frozen); + } + + [Fact] + public void Set_AddsValueWhenUnfrozen() + { + var dict = new JsonDictionary(); + + dict.Set("name", "Alice"); + dict.Set("age", 30); + + var frozen = dict.Freeze(); + Assert.Equal("Alice", frozen["name"].GetString()); + Assert.Equal(30, frozen["age"].GetInt32()); + } + + [Fact] + public void Set_OverwritesExistingValue() + { + var dict = new JsonDictionary(); + + dict.Set("foo", "bar"); + dict.Set("foo", "baz"); + + var frozen = dict.Freeze(); + Assert.Equal("baz", frozen["foo"].GetString()); + } + + [Fact] + public void Set_ThrowsAfterFreezing() + { + var dict = new JsonDictionary(); + dict.Freeze(); + + Assert.Throws(() => dict.Set("foo", "bar")); + } + + [Fact] + public void Freeze_ReturnsFrozenDictionary() + { + var dict = new JsonDictionary(); + dict.Set("foo", "bar"); + + var frozen1 = dict.Freeze(); + var frozen2 = dict.Freeze(); + + Assert.Same(frozen1, frozen2); + } + + [Fact] + public void GetNotNullClass_ReturnsValue() + { + var dict = new JsonDictionary(); + dict.Set("name", "Alice"); + + Assert.Equal("Alice", dict.GetNotNullClass("name")); + } + + [Fact] + public void GetNotNullClass_CachesDeserializedValue() + { + var dict = new JsonDictionary(); + dict.Set("name", "Alice"); + + var first = dict.GetNotNullClass("name"); + var second = dict.GetNotNullClass("name"); + + Assert.Same(first, second); + } + + [Fact] + public void GetNotNullClass_ThrowsWhenKeyAbsent() + { + var dict = new JsonDictionary(); + + var exception = Assert.Throws(() => + dict.GetNotNullClass("missing") + ); + Assert.Contains("'missing' cannot be absent", exception.Message); + } + + [Fact] + public void GetNotNullClass_ThrowsWhenValueIsNull() + { + var dict = new JsonDictionary( + new Dictionary + { + ["nullable"] = JsonSerializer.SerializeToElement(null), + } + ); + + var exception = Assert.Throws(() => + dict.GetNotNullClass("nullable") + ); + Assert.Contains("'nullable' cannot be null", exception.Message); + } + + [Fact] + public void GetNotNullClass_ThrowsWhenTypeInvalid() + { + var dict = new JsonDictionary(); + dict.Set("number", 42); + + var exception = Assert.Throws(() => + dict.GetNotNullClass("number") + ); + Assert.Contains("'number' must be of type", exception.Message); + } + + [Fact] + public void GetNotNullStruct_ReturnsValue() + { + var dict = new JsonDictionary(); + dict.Set("age", 30); + + var age = dict.GetNotNullStruct("age"); + + Assert.Equal(30, age); + } + + [Fact] + public void GetNotNullStruct_ThrowsWhenKeyAbsent() + { + var dict = new JsonDictionary(); + + var exception = Assert.Throws(() => + dict.GetNotNullStruct("missing") + ); + Assert.Contains("'missing' cannot be absent", exception.Message); + } + + [Fact] + public void GetNotNullStruct_ThrowsWhenValueIsNull() + { + var dict = new JsonDictionary( + new Dictionary + { + ["nullable"] = JsonSerializer.SerializeToElement(null), + } + ); + + var exception = Assert.Throws(() => + dict.GetNotNullStruct("nullable") + ); + Assert.Contains("'nullable' cannot be null", exception.Message); + } + + [Fact] + public void GetNotNullStruct_ThrowsWhenTypeInvalid() + { + var dict = new JsonDictionary(); + dict.Set("text", "not a number"); + + var exception = Assert.Throws(() => + dict.GetNotNullStruct("text") + ); + Assert.Contains("'text' must be of type", exception.Message); + } + + [Fact] + public void GetNullableClass_ReturnsValueWhenPresent() + { + var dict = new JsonDictionary(); + dict.Set("name", "Alice"); + + var name = dict.GetNullableClass("name"); + + Assert.Equal("Alice", name); + } + + [Fact] + public void GetNullableClass_ReturnsNullWhenKeyAbsent() + { + var dict = new JsonDictionary(); + + var missing = dict.GetNullableClass("missing"); + + Assert.Null(missing); + } + + [Fact] + public void GetNullableClass_ReturnsNullWhenValueIsNull() + { + var dict = new JsonDictionary( + new Dictionary + { + ["nullable"] = JsonSerializer.SerializeToElement(null), + } + ); + + var nullable = dict.GetNullableClass("nullable"); + + Assert.Null(nullable); + } + + [Fact] + public void GetNullableClass_CachesDeserializedValue() + { + var dict = new JsonDictionary(); + dict.Set("name", "Alice"); + + var first = dict.GetNullableClass("name"); + var second = dict.GetNullableClass("name"); + + Assert.Same(first, second); + } + + [Fact] + public void GetNullableClass_ThrowsWhenTypeInvalid() + { + var dict = new JsonDictionary(); + dict.Set("number", 42); + + var exception = Assert.Throws(() => + dict.GetNullableClass("number") + ); + Assert.Contains("'number' must be of type", exception.Message); + } + + [Fact] + public void GetNullableStruct_ReturnsValueWhenPresent() + { + var dict = new JsonDictionary(); + dict.Set("age", 30); + + var age = dict.GetNullableStruct("age"); + + Assert.Equal(30, age); + } + + [Fact] + public void GetNullableStruct_ReturnsNullWhenKeyAbsent() + { + var dict = new JsonDictionary(); + + var missing = dict.GetNullableStruct("missing"); + + Assert.Null(missing); + } + + [Fact] + public void GetNullableStruct_ReturnsNullWhenValueIsNull() + { + var dict = new JsonDictionary( + new Dictionary + { + ["nullable"] = JsonSerializer.SerializeToElement(null), + } + ); + + var nullable = dict.GetNullableStruct("nullable"); + + Assert.Null(nullable); + } + + [Fact] + public void GetNullableStruct_ThrowsWhenTypeInvalid() + { + var dict = new JsonDictionary(); + dict.Set("text", "not a number"); + + var exception = Assert.Throws(() => + dict.GetNullableStruct("text") + ); + Assert.Contains("'text' must be of type", exception.Message); + } + + [Fact] + public void ComplexWorkflow_SetFreezeAndGet() + { + var dict = new JsonDictionary(); + dict.Set("name", "Alice"); + dict.Set("age", 30); + dict.Set("active", true); + + var frozen = dict.Freeze(); + + Assert.Equal("Alice", dict.GetNotNullClass("name")); + Assert.Equal(30, dict.GetNotNullStruct("age")); + Assert.True(dict.GetNotNullStruct("active")); + Assert.Null(dict.GetNullableClass("missing")); + + Assert.Throws(() => dict.Set("new", "value")); + } + + [Fact] + public void ToString_ContainsJsonValues() + { + var dict = new JsonDictionary(); + dict.Set("name", "Alice"); + dict.Set("age", 30); + + var json = dict.ToString(); + + Assert.Contains("\"name\"", json); + Assert.Contains("\"Alice\"", json); + Assert.Contains("\"age\"", json); + Assert.Contains("30", json); + } + + [Fact] + public void Equals_ReturnsTrueForSameContent() + { + var dict1 = new JsonDictionary(); + dict1.Set("name", "Alice"); + dict1.Set("age", 30); + + var dict2 = new JsonDictionary(); + dict2.Set("name", "Alice"); + dict2.Set("age", 30); + + Assert.True(dict1.Equals(dict2)); + } + + [Fact] + public void Equals_ReturnsFalseForDifferentContent() + { + var dict1 = new JsonDictionary(); + dict1.Set("name", "Alice"); + + var dict2 = new JsonDictionary(); + dict2.Set("name", "Bob"); + + Assert.False(dict1.Equals(dict2)); + } + + [Fact] + public void Equals_ReturnsFalseForDifferentCounts() + { + var dict1 = new JsonDictionary(); + dict1.Set("name", "Alice"); + dict1.Set("age", 30); + + var dict2 = new JsonDictionary(); + dict2.Set("name", "Alice"); + + Assert.False(dict1.Equals(dict2)); + } + + [Fact] + public void Equals_ReturnsFalseForNull() + { + var dict = new JsonDictionary(); + dict.Set("name", "Alice"); + + Assert.False(dict.Equals(null)); + } + + [Fact] + public void Equals_ReturnsFalseForDifferentType() + { + var dict = new JsonDictionary(); + dict.Set("name", "Alice"); + + Assert.False(dict.Equals("not a dictionary")); + } +} diff --git a/src/Imagekit.Tests/Core/MultipartJsonDictionaryTest.cs b/src/Imagekit.Tests/Core/MultipartJsonDictionaryTest.cs new file mode 100644 index 00000000..705ca35f --- /dev/null +++ b/src/Imagekit.Tests/Core/MultipartJsonDictionaryTest.cs @@ -0,0 +1,333 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using Imagekit.Core; +using Imagekit.Exceptions; + +namespace Imagekit.Tests.Core; + +public class MultipartJsonDictionaryTest : TestBase +{ + [Fact] + public void DefaultConstructor_CreatesEmptyDictionary() + { + var dict = new MultipartJsonDictionary(); + + Assert.Empty(dict.Freeze()); + } + + [Fact] + public void DictionaryConstructor_CopiesData() + { + var source = new Dictionary + { + ["foo"] = MultipartJsonSerializer.SerializeToElement("bar"), + ["bar"] = MultipartJsonSerializer.SerializeToElement(42), + }; + + var dict = new MultipartJsonDictionary(source); + + var frozen = dict.Freeze(); + Assert.Equal(2, frozen.Count); + Assert.Equal("bar", frozen["foo"].Json.GetString()); + Assert.Equal(42, frozen["bar"].Json.GetInt32()); + } + + [Fact] + public void FrozenDictionaryConstructor_UsesProvidedDictionary() + { + var source = FrozenDictionary.ToFrozenDictionary( + new Dictionary + { + ["foo"] = MultipartJsonSerializer.SerializeToElement("bar"), + } + ); + + var dict = new MultipartJsonDictionary(source); + + var frozen = dict.Freeze(); + Assert.Same(source, frozen); + } + + [Fact] + public void Set_AddsValueWhenUnfrozen() + { + var dict = new MultipartJsonDictionary(); + + dict.Set("name", "Alice"); + dict.Set("age", 30); + + var frozen = dict.Freeze(); + Assert.Equal("Alice", frozen["name"].Json.GetString()); + Assert.Equal(30, frozen["age"].Json.GetInt32()); + } + + [Fact] + public void Set_OverwritesExistingValue() + { + var dict = new MultipartJsonDictionary(); + + dict.Set("foo", "bar"); + dict.Set("foo", "baz"); + + var frozen = dict.Freeze(); + Assert.Equal("baz", frozen["foo"].Json.GetString()); + } + + [Fact] + public void Set_ThrowsAfterFreezing() + { + var dict = new MultipartJsonDictionary(); + dict.Freeze(); + + Assert.Throws(() => dict.Set("foo", "bar")); + } + + [Fact] + public void Freeze_ReturnsFrozenDictionary() + { + var dict = new MultipartJsonDictionary(); + dict.Set("foo", "bar"); + + var frozen1 = dict.Freeze(); + var frozen2 = dict.Freeze(); + + Assert.Same(frozen1, frozen2); + } + + [Fact] + public void GetNotNullClass_ReturnsValue() + { + var dict = new MultipartJsonDictionary(); + dict.Set("name", "Alice"); + + Assert.Equal("Alice", dict.GetNotNullClass("name")); + } + + [Fact] + public void GetNotNullClass_CachesDeserializedValue() + { + var dict = new MultipartJsonDictionary(); + dict.Set("name", "Alice"); + + var first = dict.GetNotNullClass("name"); + var second = dict.GetNotNullClass("name"); + + Assert.Same(first, second); + } + + [Fact] + public void GetNotNullClass_ThrowsWhenKeyAbsent() + { + var dict = new MultipartJsonDictionary(); + + var exception = Assert.Throws(() => + dict.GetNotNullClass("missing") + ); + Assert.Contains("'missing' cannot be absent", exception.Message); + } + + [Fact] + public void GetNotNullClass_ThrowsWhenValueIsNull() + { + var dict = new MultipartJsonDictionary( + new Dictionary + { + ["nullable"] = MultipartJsonSerializer.SerializeToElement(null), + } + ); + + var exception = Assert.Throws(() => + dict.GetNotNullClass("nullable") + ); + Assert.Contains("'nullable' cannot be null", exception.Message); + } + + [Fact] + public void GetNotNullClass_ThrowsWhenTypeInvalid() + { + var dict = new MultipartJsonDictionary(); + dict.Set("number", 42); + + var exception = Assert.Throws(() => + dict.GetNotNullClass("number") + ); + Assert.Contains("'number' must be of type", exception.Message); + } + + [Fact] + public void GetNotNullStruct_ReturnsValue() + { + var dict = new MultipartJsonDictionary(); + dict.Set("age", 30); + + var age = dict.GetNotNullStruct("age"); + + Assert.Equal(30, age); + } + + [Fact] + public void GetNotNullStruct_ThrowsWhenKeyAbsent() + { + var dict = new MultipartJsonDictionary(); + + var exception = Assert.Throws(() => + dict.GetNotNullStruct("missing") + ); + Assert.Contains("'missing' cannot be absent", exception.Message); + } + + [Fact] + public void GetNotNullStruct_ThrowsWhenValueIsNull() + { + var dict = new MultipartJsonDictionary( + new Dictionary + { + ["nullable"] = MultipartJsonSerializer.SerializeToElement(null), + } + ); + + var exception = Assert.Throws(() => + dict.GetNotNullStruct("nullable") + ); + Assert.Contains("'nullable' cannot be null", exception.Message); + } + + [Fact] + public void GetNotNullStruct_ThrowsWhenTypeInvalid() + { + var dict = new MultipartJsonDictionary(); + dict.Set("text", "not a number"); + + var exception = Assert.Throws(() => + dict.GetNotNullStruct("text") + ); + Assert.Contains("'text' must be of type", exception.Message); + } + + [Fact] + public void GetNullableClass_ReturnsValueWhenPresent() + { + var dict = new MultipartJsonDictionary(); + dict.Set("name", "Alice"); + + var name = dict.GetNullableClass("name"); + + Assert.Equal("Alice", name); + } + + [Fact] + public void GetNullableClass_ReturnsNullWhenKeyAbsent() + { + var dict = new MultipartJsonDictionary(); + + var missing = dict.GetNullableClass("missing"); + + Assert.Null(missing); + } + + [Fact] + public void GetNullableClass_ReturnsNullWhenValueIsNull() + { + var dict = new MultipartJsonDictionary( + new Dictionary + { + ["nullable"] = MultipartJsonSerializer.SerializeToElement(null), + } + ); + + var nullable = dict.GetNullableClass("nullable"); + + Assert.Null(nullable); + } + + [Fact] + public void GetNullableClass_CachesDeserializedValue() + { + var dict = new MultipartJsonDictionary(); + dict.Set("name", "Alice"); + + var first = dict.GetNullableClass("name"); + var second = dict.GetNullableClass("name"); + + Assert.Same(first, second); + } + + [Fact] + public void GetNullableClass_ThrowsWhenTypeInvalid() + { + var dict = new MultipartJsonDictionary(); + dict.Set("number", 42); + + var exception = Assert.Throws(() => + dict.GetNullableClass("number") + ); + Assert.Contains("'number' must be of type", exception.Message); + } + + [Fact] + public void GetNullableStruct_ReturnsValueWhenPresent() + { + var dict = new MultipartJsonDictionary(); + dict.Set("age", 30); + + var age = dict.GetNullableStruct("age"); + + Assert.Equal(30, age); + } + + [Fact] + public void GetNullableStruct_ReturnsNullWhenKeyAbsent() + { + var dict = new MultipartJsonDictionary(); + + var missing = dict.GetNullableStruct("missing"); + + Assert.Null(missing); + } + + [Fact] + public void GetNullableStruct_ReturnsNullWhenValueIsNull() + { + var dict = new MultipartJsonDictionary( + new Dictionary + { + ["nullable"] = MultipartJsonSerializer.SerializeToElement(null), + } + ); + + var nullable = dict.GetNullableStruct("nullable"); + + Assert.Null(nullable); + } + + [Fact] + public void GetNullableStruct_ThrowsWhenTypeInvalid() + { + var dict = new MultipartJsonDictionary(); + dict.Set("text", "not a number"); + + var exception = Assert.Throws(() => + dict.GetNullableStruct("text") + ); + Assert.Contains("'text' must be of type", exception.Message); + } + + [Fact] + public void ComplexWorkflow_SetFreezeAndGet() + { + var dict = new MultipartJsonDictionary(); + dict.Set("name", "Alice"); + dict.Set("age", 30); + dict.Set("active", true); + + var frozen = dict.Freeze(); + + Assert.Equal("Alice", dict.GetNotNullClass("name")); + Assert.Equal(30, dict.GetNotNullStruct("age")); + Assert.True(dict.GetNotNullStruct("active")); + Assert.Null(dict.GetNullableClass("missing")); + + Assert.Throws(() => dict.Set("new", "value")); + } +} diff --git a/src/Imagekit.Tests/Core/MultipartJsonElementTest.cs b/src/Imagekit.Tests/Core/MultipartJsonElementTest.cs new file mode 100644 index 00000000..73545f05 --- /dev/null +++ b/src/Imagekit.Tests/Core/MultipartJsonElementTest.cs @@ -0,0 +1,210 @@ +using System.Collections.Generic; +using System.Text; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Tests.Core; + +public class MultipartJsonElementTest +{ + [Fact] + public void NumberAndNumberEqual_Works() + { + MultipartJsonElement a = JsonSerializer.SerializeToElement(3); + MultipartJsonElement b = JsonSerializer.SerializeToElement(3); + Assert.True(MultipartJsonElement.DeepEquals(a, b)); + } + + [Fact] + public void NumberAndNumberNotEqual_Works() + { + MultipartJsonElement a = JsonSerializer.SerializeToElement(3); + MultipartJsonElement b = JsonSerializer.SerializeToElement(4); + Assert.False(MultipartJsonElement.DeepEquals(a, b)); + } + + [Fact] + public void StringAndStringEqual_Works() + { + MultipartJsonElement a = JsonSerializer.SerializeToElement("text"); + MultipartJsonElement b = JsonSerializer.SerializeToElement("text"); + Assert.True(MultipartJsonElement.DeepEquals(a, b)); + } + + [Fact] + public void StringAndStringNotEqual_Works() + { + MultipartJsonElement a = JsonSerializer.SerializeToElement("text"); + MultipartJsonElement b = JsonSerializer.SerializeToElement("test"); + Assert.False(MultipartJsonElement.DeepEquals(a, b)); + } + + [Fact] + public void StringAndNumberNotEqual1_Works() + { + MultipartJsonElement a = JsonSerializer.SerializeToElement("text"); + MultipartJsonElement b = JsonSerializer.SerializeToElement(3); + Assert.False(MultipartJsonElement.DeepEquals(a, b)); + } + + [Fact] + public void StringAndNumberNotEqual1_Works1() + { + MultipartJsonElement a = JsonSerializer.SerializeToElement("text"); + MultipartJsonElement b = JsonSerializer.SerializeToElement(3); + Assert.False(MultipartJsonElement.DeepEquals(a, b)); + } + + [Fact] + public void StringAndNumberNotEqual2_Works() + { + MultipartJsonElement a = JsonSerializer.SerializeToElement("3"); + MultipartJsonElement b = JsonSerializer.SerializeToElement(3); + Assert.False(MultipartJsonElement.DeepEquals(a, b)); + } + + [Fact] + public void BinaryContentEqual_Works() + { + BinaryContent content = Encoding.UTF8.GetBytes("text"); + + MultipartJsonElement a = MultipartJsonSerializer.SerializeToElement(content); + MultipartJsonElement b = MultipartJsonSerializer.SerializeToElement(content); + + Assert.True(MultipartJsonElement.DeepEquals(a, b)); + } + + [Fact] + public void BinaryContentDifferentReferencesNotEqual_Works() + { + BinaryContent contentA = Encoding.UTF8.GetBytes("text"); + BinaryContent contentB = Encoding.UTF8.GetBytes("text"); + + MultipartJsonElement a = MultipartJsonSerializer.SerializeToElement(contentA); + MultipartJsonElement b = MultipartJsonSerializer.SerializeToElement(contentB); + + Assert.False(MultipartJsonElement.DeepEquals(a, b)); + } + + [Fact] + public void ArraysEqual_Works() + { + BinaryContent content1 = Encoding.UTF8.GetBytes("text"); + BinaryContent content2 = Encoding.UTF8.GetBytes("text"); + BinaryContent content3 = Encoding.UTF8.GetBytes("text"); + + MultipartJsonElement a = MultipartJsonSerializer.SerializeToElement( + new List { content1, content2, content3 } + ); + MultipartJsonElement b = MultipartJsonSerializer.SerializeToElement( + new List { content1, content2, content3 } + ); + + Assert.True(MultipartJsonElement.DeepEquals(a, b)); + } + + [Fact] + public void ArrayMissingElementNotEqual_Works() + { + BinaryContent content1 = Encoding.UTF8.GetBytes("text"); + BinaryContent content2 = Encoding.UTF8.GetBytes("text"); + BinaryContent content3 = Encoding.UTF8.GetBytes("text"); + + MultipartJsonElement a = MultipartJsonSerializer.SerializeToElement( + new List { content1, content2, content3 } + ); + MultipartJsonElement b = MultipartJsonSerializer.SerializeToElement( + new List { content1, content2 } + ); + + Assert.False(MultipartJsonElement.DeepEquals(a, b)); + } + + [Fact] + public void ArrayOutOfOrderNotEqual_Works() + { + BinaryContent content1 = Encoding.UTF8.GetBytes("text"); + BinaryContent content2 = Encoding.UTF8.GetBytes("text"); + BinaryContent content3 = Encoding.UTF8.GetBytes("text"); + + MultipartJsonElement a = MultipartJsonSerializer.SerializeToElement( + new List { content1, content2, content3 } + ); + MultipartJsonElement b = MultipartJsonSerializer.SerializeToElement( + new List { content1, content3, content2 } + ); + + Assert.False(MultipartJsonElement.DeepEquals(a, b)); + } + + [Fact] + public void ObjectsEqual_Works() + { + BinaryContent content = Encoding.UTF8.GetBytes("text"); + + MultipartJsonElement a = MultipartJsonSerializer.SerializeToElement( + new Dictionary + { + { "string", "text" }, + { "number", -5 }, + { "binary", content }, + } + ); + MultipartJsonElement b = MultipartJsonSerializer.SerializeToElement( + new Dictionary + { + { "string", "text" }, + { "number", -5 }, + { "binary", content }, + } + ); + + Assert.True(MultipartJsonElement.DeepEquals(a, b)); + } + + [Fact] + public void ObjectExtraKeyNotEqual_Works() + { + BinaryContent content = Encoding.UTF8.GetBytes("text"); + + MultipartJsonElement a = MultipartJsonSerializer.SerializeToElement( + new Dictionary + { + { "string", "text" }, + { "number", -5 }, + { "binary", content }, + } + ); + MultipartJsonElement b = MultipartJsonSerializer.SerializeToElement( + new Dictionary + { + { "string", "text" }, + { "number", -5 }, + { "binary", content }, + { "extra", "test" }, + } + ); + + Assert.False(MultipartJsonElement.DeepEquals(a, b)); + } + + [Fact] + public void ObjectExtraKeyNotEqual_Works1() + { + BinaryContent content = Encoding.UTF8.GetBytes("text"); + + MultipartJsonElement a = MultipartJsonSerializer.SerializeToElement( + new Dictionary + { + { "string", "text" }, + { "number", -5 }, + { "binary", content }, + } + ); + MultipartJsonElement b = MultipartJsonSerializer.SerializeToElement( + new Dictionary { { "string", "text" }, { "binary", content } } + ); + + Assert.False(MultipartJsonElement.DeepEquals(a, b)); + } +} diff --git a/src/Imagekit.Tests/Helper/HelperAdvancedTransformationsTest.cs b/src/Imagekit.Tests/Helper/HelperAdvancedTransformationsTest.cs new file mode 100644 index 00000000..599c1831 --- /dev/null +++ b/src/Imagekit.Tests/Helper/HelperAdvancedTransformationsTest.cs @@ -0,0 +1,456 @@ +// Go source: tests/helper_advanced_transformations_test.go +// Total test cases in Go: 20 (TestAITransformations: 10, TestParameterSpecificTransformations: 10) + +using System.Collections.Generic; +using Imagekit; +using Imagekit.Models; + +namespace Imagekit.Tests.Helper; + +public class HelperAdvancedTransformationsTest +{ + private static readonly ImageKitClient _client = new() { PrivateKey = "My Private API Key" }; + + // --- TestAITransformations --- + + [Fact] + public void AIRemoveBackground_True_GeneratesEBgremove() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path1.jpg", + Transformation = [new Transformation { AIRemoveBackground = true }], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-bgremove", url); + } + + [Fact] + public void AIRemoveBackgroundExternal_True_GeneratesERemovedotbg() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path1.jpg", + Transformation = [new Transformation { AIRemoveBackgroundExternal = true }], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-removedotbg", + url + ); + } + + [Fact] + public void AIDropShadow_True_GeneratesEDropshadow() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path1.jpg", + Transformation = [new Transformation { AIDropShadow = new AIDropShadowDefault() }], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-dropshadow", + url + ); + } + + [Fact] + public void Gradient_True_GeneratesEGradient() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path1.jpg", + Transformation = + [ + new Transformation { Gradient = new TransformationGradientDefault() }, + ], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-gradient", url); + } + + [Fact] + public void AIRemoveBackground_NotSet_OmitsTrParam() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path1.jpg", + Transformation = [new Transformation()], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/test_path1.jpg", url); + } + + [Fact] + public void AIRemoveBackgroundExternal_NotSet_OmitsTrParam() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path1.jpg", + Transformation = [new Transformation()], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/test_path1.jpg", url); + } + + [Fact] + public void AIDropShadow_WithCustomParams_GeneratesEDropshadowWithParams() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path1.jpg", + Transformation = [new Transformation { AIDropShadow = "custom-shadow-params" }], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-dropshadow-custom-shadow-params", + url + ); + } + + [Fact] + public void Gradient_WithParams_GeneratesEGradientWithParams() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path1.jpg", + Transformation = + [ + new Transformation { Gradient = "ld-top_from-green_to-00FF0010_sp-1" }, + ], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-gradient-ld-top_from-green_to-00FF0010_sp-1", + url + ); + } + + [Fact] + public void AIRemoveBackground_WithWidthAndHeight_IncludesAllTransforms() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path1.jpg", + Transformation = + [ + new Transformation + { + Width = 300.0, + Height = 200.0, + AIRemoveBackground = true, + }, + ], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=w-300,h-200,e-bgremove", + url + ); + } + + [Fact] + public void AIRemoveBackground_WithDropShadow_IncludesBothTransforms() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path1.jpg", + Transformation = + [ + new Transformation + { + AIRemoveBackground = true, + AIDropShadow = new AIDropShadowDefault(), + }, + ], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-bgremove,e-dropshadow", + url + ); + } + + // --- TestParameterSpecificTransformations --- + + [Fact] + public void Width_NumberValue_GeneratesWParam() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path1.jpg", + Transformation = [new Transformation { Width = 400.0 }], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=w-400", url); + } + + [Fact] + public void Height_StringValue_GeneratesHParam() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path1.jpg", + Transformation = [new Transformation { Height = "300" }], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=h-300", url); + } + + [Fact] + public void AspectRatio_ColonFormat_GeneratesArParam() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path1.jpg", + Transformation = [new Transformation { AspectRatio = "4:3" }], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=ar-4:3", url); + } + + [Fact] + public void Quality_NumberValue_GeneratesQParam() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path1.jpg", + Transformation = [new Transformation { Quality = 80.0 }], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=q-80", url); + } + + [Fact] + public void Transformation_SkipsUndefinedOrEmptyParameters() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path1.jpg", + Transformation = [new Transformation { Width = 300.0 }], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=w-300", url); + } + + [Fact] + public void Trim_BooleanValue_GeneratesTrueParam() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path1.jpg", + Transformation = [new Transformation { Trim = new TrimDefault() }], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=t-true", url); + } + + [Fact] + public void DefaultImage_EmptyString_OmitsTrParam() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path1.jpg", + Transformation = [new Transformation { DefaultImage = "" }], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/test_path1.jpg", url); + } + + [Fact] + public void ComplexTransformationCombination_GeneratesCorrectUrl() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path1.jpg", + Transformation = + [ + new Transformation + { + Width = 300.0, + Height = 200.0, + Quality = 85.0, + Border = "5_FF0000", + }, + ], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=w-300,h-200,q-85,b-5_FF0000", + url + ); + } + + [Fact] + public void Radius_PerCornerStringValue_GeneratesRParam() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path1.jpg", + Transformation = [new Transformation { Radius = "10_10_max_10" }], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=r-10_10_max_10", + url + ); + } + + [Fact] + public void AllTransformations_BigTest_GeneratesCorrectUrl() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path.jpg", + Transformation = + [ + new Transformation + { + Height = 300.0, + Width = 400.0, + AspectRatio = "4-3", + Quality = 40.0, + Crop = Crop.Force, + CropMode = CropMode.Extract, + Focus = "left", + Format = Format.Jpeg, + Radius = 50.0, + Background = "A94D34", + Border = "5-A94D34", + Rotation = 90.0, + Blur = 10.0, + Named = "some_name", + Progressive = true, + Lossless = true, + Trim = 5.0, + Metadata = true, + ColorProfile = true, + DefaultImage = "/folder/file.jpg/", + Dpr = 3.0, + X = 10.0, + Y = 20.0, + XCenter = 30.0, + YCenter = 40.0, + Flip = TransformationFlip.H, + Opacity = 0.8, + Zoom = 2.0, + VideoCodec = VideoCodec.H264, + AudioCodec = AudioCodec.Aac, + StartOffset = 5.0, + EndOffset = 15.0, + Duration = 10.0, + StreamingResolutions = + [ + StreamingResolution.V1440, + StreamingResolution.V1080, + ], + Grayscale = true, + AIUpscale = true, + AIRetouch = true, + AIVariation = true, + AIRemoveBackground = true, + ContrastStretch = true, + AIDropShadow = new AIDropShadowDefault(), + AIChangeBackground = "prompt-car", + AIEdit = "prompt-make it vintage", + Shadow = "bl-15_st-40_x-10_y-N5", + Sharpen = 10.0, + UnsharpMask = "2-2-0.8-0.024", + Gradient = "from-red_to-white", + ColorReplace = "FF0000_100_0000FF", + Colorize = "co-red_in-50", + Distort = "a-45", + Original = true, + Page = "2_4", + Raw = "h-200,w-300,l-image,i-logo.png,l-end", + }, + ], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=w-400,h-300,q-40,ar-4-3,c-force,cm-extract,fo-left,f-jpeg,r-50,bg-A94D34,b-5-A94D34,cr-FF0000_100_0000FF,di-folder@@file.jpg,dpr-3,x-10,y-20,xc-30,yc-40,o-0.8,z-2,rt-90,bl-10,n-some_name,pr-true,lo-true,fl-h,t-5,md-true,cp-true,vc-h264,ac-aac,so-5,eo-15,du-10,sr-1440_1080,e-grayscale,e-upscale,e-retouch,e-genvar,e-bgremove,e-contrast,e-dropshadow,e-changebg-prompt-car,e-edit-prompt-make it vintage,e-shadow-bl-15_st-40_x-10_y-N5,e-sharpen-10,e-usm-2-2-0.8-0.024,e-gradient-from-red_to-white,e-colorize-co-red_in-50,e-distort-a-45,orig-true,pg-2_4,h-200,w-300,l-image,i-logo.png,l-end", + url + ); + } +} diff --git a/src/Imagekit.Tests/Helper/HelperAuthenticationTest.cs b/src/Imagekit.Tests/Helper/HelperAuthenticationTest.cs new file mode 100644 index 00000000..0a512c55 --- /dev/null +++ b/src/Imagekit.Tests/Helper/HelperAuthenticationTest.cs @@ -0,0 +1,76 @@ +// Go source: tests/helper_authentication_test.go +// Total test cases in Go: 5 + +using System; +using System.Text.RegularExpressions; +using Imagekit; +using Imagekit.Models; + +namespace Imagekit.Tests.Helper; + +public class HelperAuthenticationTest +{ + private static readonly ImageKitClient _client = new() { PrivateKey = "private_key_test" }; + + [Fact] + public void GetAuthenticationParameters_WithTokenAndExpire_ReturnsCorrectParams() + { + var token = "your_token"; + var expire = 1582269249L; + + var auth = _client.Helper.GetAuthenticationParameters(token, expire); + + Assert.Equal(token, auth.Token); + Assert.Equal(expire, auth.Expire); + Assert.Equal("e71bcd6031016b060d349d212e23e85c791decdd", auth.Signature); + } + + [Fact] + public void GetAuthenticationParameters_NoParams_ReturnsRequiredProperties() + { + var auth = _client.Helper.GetAuthenticationParameters(); + + Assert.Matches(new Regex("^[0-9a-f]{32}$"), auth.Token); + Assert.True(auth.Expire > DateTimeOffset.UtcNow.ToUnixTimeSeconds()); + Assert.Matches(new Regex("^[a-f0-9]{40}$"), auth.Signature); + } + + [Fact] + public void GetAuthenticationParameters_ExpireZero_UsesDefaultExpire() + { + var token = "test-token"; + + var auth = _client.Helper.GetAuthenticationParameters(token, 0L); + + Assert.Equal(token, auth.Token); + var expectedExpire = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + 60 * 30; + Assert.InRange(auth.Expire, expectedExpire - 10, expectedExpire + 10); + Assert.Matches(new Regex("^[a-f0-9]{40}$"), auth.Signature); + } + + [Fact] + public void GetAuthenticationParameters_EmptyStringToken_GeneratesToken() + { + var expire = 1582269249L; + + var auth = _client.Helper.GetAuthenticationParameters("", expire); + + Assert.Matches(new Regex("^[0-9a-f]{32}$"), auth.Token); + Assert.Equal(expire, auth.Expire); + Assert.Matches(new Regex("^[a-f0-9]{40}$"), auth.Signature); + } + + [Fact] + public void GetAuthenticationParameters_NoPrivateKey_ThrowsWithMessage() + { + var noKeyClient = new ImageKitClient { PrivateKey = "" }; + + var ex = Assert.ThrowsAny(() => + noKeyClient.Helper.GetAuthenticationParameters("test", 123L) + ); + Assert.Equal( + "private API key is required for authentication parameters generation", + ex.Message + ); + } +} diff --git a/src/Imagekit.Tests/Helper/HelperBasicTest.cs b/src/Imagekit.Tests/Helper/HelperBasicTest.cs new file mode 100644 index 00000000..feba045f --- /dev/null +++ b/src/Imagekit.Tests/Helper/HelperBasicTest.cs @@ -0,0 +1,413 @@ +// Go source: tests/helper_basic_test.go +// Total test cases in Go: 21 + +using System.Collections.Generic; +using Imagekit; +using Imagekit.Models; + +namespace Imagekit.Tests.Helper; + +public class HelperBasicTest +{ + private static readonly ImageKitClient _client = new() { PrivateKey = "My Private API Key" }; + + [Fact] + public void BuildUrl_EmptySrc_ReturnsEmpty() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "", + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal("", url); + } + + [Fact] + public void BuildUrl_SrcIsSlash_GeneratesValidUrl() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/", + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/", url); + } + + [Fact] + public void BuildUrl_SrcWithoutTransformation_GeneratesValidUrl() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path.jpg", + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/test_path.jpg", url); + } + + [Fact] + public void BuildUrl_AbsoluteSrcWithoutTransformation_ReturnsAbsoluteUrl() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg", + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg", url); + } + + [Fact] + public void BuildUrl_UndefinedTransformationWithQuery_GeneratesValidUrl() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path_alt.jpg", + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg", url); + } + + [Fact] + public void BuildUrl_DefaultPositionIsQuery() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path.jpg", + Transformation = + [ + new Transformation { Width = "400", Height = "300" }, + new Transformation { Rotation = 90.0 }, + ], + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=w-400,h-300:rt-90", + url + ); + } + + [Fact] + public void BuildUrl_TransformationPositionPath_EmbedsTrInPath() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path.jpg", + Transformation = [new Transformation { Width = "400", Height = "300" }], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/tr:w-400,h-300/test_path.jpg", url); + } + + [Fact] + public void BuildUrl_WithTransformationPositionQuery_AddsQueryParam() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path.jpg", + Transformation = [new Transformation { Width = "400", Height = "300" }], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=w-400,h-300", url); + } + + [Fact] + public void BuildUrl_AbsoluteSrcWithPathPosition_ForcesTrAsQuery() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "https://my.custom.domain.com/test_path.jpg", + Transformation = [new Transformation { Width = "400", Height = "300" }], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal("https://my.custom.domain.com/test_path.jpg?tr=w-400,h-300", url); + } + + [Fact] + public void BuildUrl_NonDefaultUrlEndpoint_GeneratesCorrectUrl() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/imagekit_id/new-endpoint/", + Src = "/test_path.jpg", + Transformation = [new Transformation { Width = "400", Height = "300" }], + } + ); + + Assert.Equal( + "https://ik.imagekit.io/imagekit_id/new-endpoint/test_path.jpg?tr=w-400,h-300", + url + ); + } + + [Fact] + public void BuildUrl_MultipleLeadingSlashesInSrc_NormalizesSrc() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "///test_path.jpg", + Transformation = [new Transformation { Width = "400", Height = "300" }], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=w-400,h-300", url); + } + + [Fact] + public void BuildUrl_OverriddenUrlEndpoint_UsesNewEndpoint() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint_alt", + Src = "/test_path.jpg", + Transformation = [new Transformation { Width = "400", Height = "300" }], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint_alt/test_path.jpg?tr=w-400,h-300", + url + ); + } + + [Fact] + public void BuildUrl_QueryPositionExplicit_AddsQueryParam() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path.jpg", + Transformation = [new Transformation { Width = "400", Height = "300" }], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=w-400,h-300", url); + } + + [Fact] + public void BuildUrl_AbsoluteSrcWithTransformation_AddsQueryParam() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg", + Transformation = [new Transformation { Width = "400", Height = "300" }], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?tr=w-400,h-300", + url + ); + } + + [Fact] + public void BuildUrl_MergesExistingAndNewQueryParams() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?t1=v1", + Transformation = [new Transformation { Width = "400", Height = "300" }], + QueryParameters = new Dictionary { ["t2"] = "v2", ["t3"] = "v3" }, + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?t1=v1&t2=v2&t3=v3&tr=w-400,h-300", + url + ); + } + + [Fact] + public void BuildUrl_ChainedTransformations_UsesColonDelimiter() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path.jpg", + Transformation = + [ + new Transformation { Width = "400", Height = "300" }, + new Transformation { Rotation = "90" }, + ], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=w-400,h-300:rt-90", + url + ); + } + + [Fact] + public void BuildUrl_ChainedTransformationsWithRaw_AddsRawAtEnd() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path.jpg", + Transformation = + [ + new Transformation { Width = "400", Height = "300" }, + new Transformation { Raw = "rndm_trnsf-abcd" }, + ], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=w-400,h-300:rndm_trnsf-abcd", + url + ); + } + + [Fact] + public void BuildUrl_BorderTransformation_IncludesBorderParam() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path.jpg", + Transformation = + [ + new Transformation + { + Width = "400", + Height = "300", + Border = "20_FF0000", + }, + ], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=w-400,h-300,b-20_FF0000", + url + ); + } + + [Fact] + public void BuildUrl_EmptyRawTransformation_OmitsTrParam() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/test_path.jpg", + Transformation = [new Transformation { Raw = "" }], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/test_path.jpg", url); + } + + [Fact] + public void BuildUrl_CnameUrlEndpoint_GeneratesValidUrl() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://custom.domain.com", + Src = "/test_path.jpg", + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal("https://custom.domain.com/test_path.jpg", url); + } + + [Fact] + public void BuildUrl_CnameWithUrlPattern_GeneratesValidUrl() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://custom.domain.com/url-pattern", + Src = "/test_path.jpg", + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal("https://custom.domain.com/url-pattern/test_path.jpg", url); + } + + [Fact] + public void BuildUrl_WithCropQualityFormat_GeneratesCorrectQueryString() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/your_imagekit_id", + Src = "/path/to/image.jpg", + Transformation = + [ + new Transformation + { + Width = 400, + Height = 300, + Crop = Crop.MaintainRatio, + Quality = 80, + Format = Format.Webp, + }, + ], + } + ); + + Assert.Equal( + "https://ik.imagekit.io/your_imagekit_id/path/to/image.jpg?tr=w-400,h-300,q-80,c-maintain_ratio,f-webp", + url + ); + } +} diff --git a/src/Imagekit.Tests/Helper/HelperOverlayTest.cs b/src/Imagekit.Tests/Helper/HelperOverlayTest.cs new file mode 100644 index 00000000..c7fba8ea --- /dev/null +++ b/src/Imagekit.Tests/Helper/HelperOverlayTest.cs @@ -0,0 +1,855 @@ +// Go source: tests/helper_overlay_test.go +// Total test cases in Go: 31 (TestOverlayTransformations: 12, TestOverlayEncoding: 19) + +using Imagekit; +using Imagekit.Models; + +namespace Imagekit.Tests.Helper; + +public class HelperOverlayTest +{ + private static readonly ImageKitClient _client = new() { PrivateKey = "My Private API Key" }; + + // --- TestOverlayTransformations --- + + [Fact] + public void NoOverlay_JustWidth_PathPosition_GeneratesUrlWithTr() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/base-image.jpg", + Transformation = [new Transformation { Width = 300.0 }], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/tr:w-300/base-image.jpg", url); + } + + [Fact] + public void TextOverlay_EmptyText_IgnoresOverlay() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/base-image.jpg", + Transformation = [new Transformation { Overlay = new TextOverlay { Text = "" } }], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/base-image.jpg", url); + } + + [Fact] + public void ImageOverlay_EmptyInput_IgnoresOverlay() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/base-image.jpg", + Transformation = [new Transformation { Overlay = new ImageOverlay { Input = "" } }], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/base-image.jpg", url); + } + + [Fact] + public void VideoOverlay_EmptyInput_IgnoresOverlay() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/base-image.jpg", + Transformation = [new Transformation { Overlay = new VideoOverlay { Input = "" } }], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/base-image.jpg", url); + } + + [Fact] + public void SubtitleOverlay_EmptyInput_IgnoresOverlay() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/base-image.jpg", + Transformation = + [ + new Transformation { Overlay = new SubtitleOverlay { Input = "" } }, + ], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/base-image.jpg", url); + } + + [Fact] + public void SolidColorOverlay_EmptyColor_IgnoresOverlay() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/base-image.jpg", + Transformation = + [ + new Transformation { Overlay = new SolidColorOverlay { Color = "" } }, + ], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal("https://ik.imagekit.io/test_url_endpoint/base-image.jpg", url); + } + + [Fact] + public void TextOverlay_MinimalText_GeneratesUrlEncodedLayer() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/base-image.jpg", + Transformation = [new Transformation { Overlay = new TextOverlay("Minimal Text") }], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-Minimal%20Text,l-end/base-image.jpg", + url + ); + } + + [Fact] + public void ImageOverlay_LogoPng_GeneratesLayer() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/base-image.jpg", + Transformation = [new Transformation { Overlay = new ImageOverlay("logo.png") }], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-logo.png,l-end/base-image.jpg", + url + ); + } + + [Fact] + public void VideoOverlay_PlayPauseLoop_GeneratesLayer() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/base-video.mp4", + Transformation = + [ + new Transformation { Overlay = new VideoOverlay("play-pause-loop.mp4") }, + ], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/tr:l-video,i-play-pause-loop.mp4,l-end/base-video.mp4", + url + ); + } + + [Fact] + public void SubtitleOverlay_SubtitleSrt_GeneratesLayer() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/base-video.mp4", + Transformation = + [ + new Transformation { Overlay = new SubtitleOverlay("subtitle.srt") }, + ], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/tr:l-subtitles,i-subtitle.srt,l-end/base-video.mp4", + url + ); + } + + [Fact] + public void SolidColorOverlay_FF0000_GeneratesCanvasLayer() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/base-image.jpg", + Transformation = [new Transformation { Overlay = new SolidColorOverlay("FF0000") }], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-ik_canvas,bg-FF0000,l-end/base-image.jpg", + url + ); + } + + [Fact] + public void MultipleComplexOverlaysWithNestedTransformations_GeneratesCorrectUrl() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/base-image.jpg", + Transformation = + [ + new Transformation + { + Overlay = new TextOverlay("Every thing") + { + Position = new OverlayPosition + { + X = "10", + Y = "20", + Focus = Focus.Center, + }, + Timing = new OverlayTiming + { + Start = 5.0, + Duration = "10", + End = 15.0, + }, + Transformation = + [ + new TextOverlayTransformation + { + Width = "bw_mul_0.5", + FontSize = 20.0, + FontFamily = "Arial", + FontColor = "0000ff", + InnerAlignment = InnerAlignment.Left, + Padding = 5.0, + Alpha = 7.0, + Typography = "b", + Background = "red", + Radius = 10.0, + Rotation = "N45", + Flip = Flip.H, + LineHeight = 20.0, + }, + ], + }, + }, + new Transformation + { + Overlay = new ImageOverlay("logo.png") + { + Position = new OverlayPosition + { + X = "10", + Y = "20", + Focus = Focus.Center, + }, + Timing = new OverlayTiming + { + Start = 5.0, + Duration = "10", + End = 15.0, + }, + Transformation = + [ + new Transformation + { + Width = "bw_mul_0.5", + Height = "bh_mul_0.5", + Rotation = "N45", + Flip = TransformationFlip.H, + Overlay = new TextOverlay("Nested text overlay"), + }, + ], + }, + }, + new Transformation + { + Overlay = new VideoOverlay("play-pause-loop.mp4") + { + Position = new OverlayPosition + { + X = "10", + Y = "20", + Focus = Focus.Center, + }, + Timing = new OverlayTiming + { + Start = 5.0, + Duration = "10", + End = 15.0, + }, + Transformation = + [ + new Transformation + { + Width = "bw_mul_0.5", + Height = "bh_mul_0.5", + Rotation = "N45", + Flip = TransformationFlip.H, + }, + ], + }, + }, + new Transformation + { + Overlay = new SubtitleOverlay("subtitle.srt") + { + Position = new OverlayPosition + { + X = "10", + Y = "20", + Focus = Focus.Center, + }, + Timing = new OverlayTiming + { + Start = 5.0, + Duration = "10", + End = 15.0, + }, + Transformation = + [ + new SubtitleOverlayTransformation + { + Background = "red", + Color = "0000ff", + FontFamily = "Arial", + FontOutline = "2_A1CCDD50", + FontShadow = "A1CCDD_3", + }, + ], + }, + }, + new Transformation + { + Overlay = new SolidColorOverlay("FF0000") + { + Position = new OverlayPosition + { + X = "10", + Y = "20", + Focus = Focus.Center, + }, + Timing = new OverlayTiming + { + Start = 5.0, + Duration = "10", + End = 15.0, + }, + Transformation = + [ + new SolidColorOverlayTransformation + { + Width = "bw_mul_0.5", + Height = "bh_mul_0.5", + Alpha = 0.5, + Background = "red", + Gradient = new Default(), + Radius = new Max(), + }, + ], + }, + }, + ], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-Every%20thing,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,fs-20,ff-Arial,co-0000ff,ia-left,pa-5,al-7,tg-b,bg-red,r-10,rt-N45,fl-h,lh-20,l-end:l-image,i-logo.png,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-text,i-Nested%20text%20overlay,l-end,l-end:l-video,i-play-pause-loop.mp4,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-end:l-subtitles,i-subtitle.srt,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,bg-red,co-0000ff,ff-Arial,fol-2_A1CCDD50,fsh-A1CCDD_3,l-end:l-image,i-ik_canvas,bg-FF0000,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,al-0.5,bg-red,e-gradient,r-max,l-end/base-image.jpg", + url + ); + } + + // --- TestOverlayEncoding --- + + [Fact] + public void ImageOverlay_SlashesInPath_ConvertedToDoubleAt() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo", + Src = "/medium_cafe_B1iTdD0C.jpg", + Transformation = + [ + new Transformation { Overlay = new ImageOverlay("/customer_logo/nykaa.png") }, + ], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/demo/tr:l-image,i-customer_logo@@nykaa.png,l-end/medium_cafe_B1iTdD0C.jpg", + url + ); + } + + [Fact] + public void ImageOverlay_SpecialCharsInPath_UsesBase64Encoding() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo", + Src = "/medium_cafe_B1iTdD0C.jpg", + Transformation = + [ + new Transformation { Overlay = new ImageOverlay("/customer_logo/Ñykaa.png") }, + ], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/demo/tr:l-image,ie-Y3VzdG9tZXJfbG9nby%2FDkXlrYWEucG5n,l-end/medium_cafe_B1iTdD0C.jpg", + url + ); + } + + [Fact] + public void TextOverlay_SimpleAscii_UsesPlainEncoding() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo", + Src = "/medium_cafe_B1iTdD0C.jpg", + Transformation = [new Transformation { Overlay = new TextOverlay("Manu") }], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/demo/tr:l-text,i-Manu,l-end/medium_cafe_B1iTdD0C.jpg", + url + ); + } + + [Fact] + public void TextOverlay_FontFamilyWithSlash_ConvertedToDoubleAt() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo", + Src = "/medium_cafe_B1iTdD0C.jpg", + Transformation = + [ + new Transformation + { + Overlay = new TextOverlay("Manu") + { + Transformation = + [ + new TextOverlayTransformation + { + FontFamily = "nested-path/Poppins-Regular_Q15GrYWmL.ttf", + }, + ], + }, + }, + ], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/demo/tr:l-text,i-Manu,ff-nested-path@@Poppins-Regular_Q15GrYWmL.ttf,l-end/medium_cafe_B1iTdD0C.jpg", + url + ); + } + + [Fact] + public void TextOverlay_SpacesAndSafeChars_UrlEncoded() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo", + Src = "/medium_cafe_B1iTdD0C.jpg", + Transformation = [new Transformation { Overlay = new TextOverlay("alnum123-._ ") }], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/demo/tr:l-text,i-alnum123-._%20,l-end/medium_cafe_B1iTdD0C.jpg", + url + ); + } + + [Fact] + public void TextOverlay_UnicodeSpecialChars_UsesBase64() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo", + Src = "/medium_cafe_B1iTdD0C.jpg", + Transformation = + [ + new Transformation { Overlay = new TextOverlay("Let's use ©, ®, ™, etc") }, + ], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/demo/tr:l-text,ie-TGV0J3MgdXNlIMKpLCDCriwg4oSiLCBldGM%3D,l-end/medium_cafe_B1iTdD0C.jpg", + url + ); + } + + [Fact] + public void TextOverlay_ExplicitPlainEncoding_UsesPlain() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo", + Src = "/sample.jpg", + Transformation = + [ + new Transformation + { + Overlay = new TextOverlay("HelloWorld") { Encoding = "plain" }, + }, + ], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal("https://ik.imagekit.io/demo/tr:l-text,i-HelloWorld,l-end/sample.jpg", url); + } + + [Fact] + public void TextOverlay_ExplicitBase64Encoding_UsesBase64() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo", + Src = "/sample.jpg", + Transformation = + [ + new Transformation + { + Overlay = new TextOverlay("HelloWorld") { Encoding = "base64" }, + }, + ], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/demo/tr:l-text,ie-SGVsbG9Xb3JsZA%3D%3D,l-end/sample.jpg", + url + ); + } + + [Fact] + public void ImageOverlay_ExplicitPlainEncoding_NormalizesSlashes() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo", + Src = "/sample.jpg", + Transformation = + [ + new Transformation + { + Overlay = new ImageOverlay("/customer/logo.png") { Encoding = "plain" }, + }, + ], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/demo/tr:l-image,i-customer@@logo.png,l-end/sample.jpg", + url + ); + } + + [Fact] + public void ImageOverlay_ExplicitBase64Encoding_UsesBase64() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo", + Src = "/sample.jpg", + Transformation = + [ + new Transformation + { + Overlay = new ImageOverlay("/customer/logo.png") { Encoding = "base64" }, + }, + ], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/demo/tr:l-image,ie-Y3VzdG9tZXIvbG9nby5wbmc%3D,l-end/sample.jpg", + url + ); + } + + [Fact] + public void VideoOverlay_ExplicitBase64Encoding_UsesBase64() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo", + Src = "/sample.mp4", + Transformation = + [ + new Transformation + { + Overlay = new VideoOverlay("/path/to/video.mp4") { Encoding = "base64" }, + }, + ], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/demo/tr:l-video,ie-cGF0aC90by92aWRlby5tcDQ%3D,l-end/sample.mp4", + url + ); + } + + [Fact] + public void SubtitleOverlay_ExplicitPlainEncoding_NormalizesSlashes() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo", + Src = "/sample.mp4", + Transformation = + [ + new Transformation + { + Overlay = new SubtitleOverlay("/sub.srt") { Encoding = "plain" }, + }, + ], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal("https://ik.imagekit.io/demo/tr:l-subtitles,i-sub.srt,l-end/sample.mp4", url); + } + + [Fact] + public void SubtitleOverlay_ExplicitBase64Encoding_UsesBase64() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo", + Src = "/sample.mp4", + Transformation = + [ + new Transformation + { + Overlay = new SubtitleOverlay("sub.srt") { Encoding = "base64" }, + }, + ], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/demo/tr:l-subtitles,ie-c3ViLnNydA%3D%3D,l-end/sample.mp4", + url + ); + } + + [Fact] + public void TextOverlay_QueryPosition_EncodesInQueryParam() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo", + Src = "/sample.jpg", + Transformation = [new Transformation { Overlay = new TextOverlay("Minimal Text") }], + TransformationPosition = TransformationPosition.Query, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/demo/sample.jpg?tr=l-text,i-Minimal%20Text,l-end", + url + ); + } + + [Fact] + public void ImageOverlay_LayerModeMultiply_GeneratesLmMultiply() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/base-image.jpg", + Transformation = + [ + new Transformation + { + Overlay = new ImageOverlay("logo.png") { LayerMode = LayerMode.Multiply }, + }, + ], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-logo.png,lm-multiply,l-end/base-image.jpg", + url + ); + } + + [Fact] + public void ImageOverlay_LayerModeCutter_GeneratesLmCutter() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/base-image.jpg", + Transformation = + [ + new Transformation + { + Overlay = new ImageOverlay("mask.png") { LayerMode = LayerMode.Cutter }, + }, + ], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-mask.png,lm-cutter,l-end/base-image.jpg", + url + ); + } + + [Fact] + public void ImageOverlay_LayerModeCutout_GeneratesLmCutout() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/base-image.jpg", + Transformation = + [ + new Transformation + { + Overlay = new ImageOverlay("shape.png") { LayerMode = LayerMode.Cutout }, + }, + ], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-shape.png,lm-cutout,l-end/base-image.jpg", + url + ); + } + + [Fact] + public void ImageOverlay_LayerModeDisplace_GeneratesLmDisplace() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/base-image.jpg", + Transformation = + [ + new Transformation + { + Overlay = new ImageOverlay("displacement.png") + { + LayerMode = LayerMode.Displace, + }, + }, + ], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-displacement.png,lm-displace,l-end/base-image.jpg", + url + ); + } + + [Fact] + public void ImageOverlay_XCenterYCenterAnchorPoint_GeneratesCorrectPositionParams() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/test_url_endpoint", + Src = "/base-image.jpg", + Transformation = + [ + new Transformation + { + Overlay = new ImageOverlay("logo.png") + { + Position = new OverlayPosition + { + XCenter = 50.0, + YCenter = "bh_mul_0.5", + AnchorPoint = AnchorPoint.TopLeft, + }, + }, + }, + ], + TransformationPosition = TransformationPosition.Path, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-logo.png,lxc-50,lyc-bh_mul_0.5,lap-top_left,l-end/base-image.jpg", + url + ); + } +} diff --git a/src/Imagekit.Tests/Helper/HelperSigningTest.cs b/src/Imagekit.Tests/Helper/HelperSigningTest.cs new file mode 100644 index 00000000..8c5c5742 --- /dev/null +++ b/src/Imagekit.Tests/Helper/HelperSigningTest.cs @@ -0,0 +1,240 @@ +// Go source: tests/helper_signing_test.go +// Total test cases in Go: 10 + +using Imagekit; +using Imagekit.Models; + +namespace Imagekit.Tests.Helper; + +public class HelperSigningTest +{ + private static readonly ImageKitClient _client = new() { PrivateKey = "dummy-key" }; + + [Fact] + public void BuildUrl_SignedWithoutExpiresIn_AddsSignatureWithoutTimestamp() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo/", + Src = "sdk-testing-files/future-search.png", + Signed = true, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/demo/sdk-testing-files/future-search.png?ik-s=32dbbbfc5f945c0403c71b54c38e76896ef2d6b0", + url + ); + } + + [Fact] + public void BuildUrl_SignedWithExpiresIn_AddsTimestampParam() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo/", + Src = "sdk-testing-files/future-search.png", + Signed = true, + ExpiresIn = 3600, + } + ); + + Assert.Contains("ik-t", url); + } + + [Fact] + public void BuildUrl_ExpiresInAboveZeroWithSignedFalse_StillAddsTimestamp() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo/", + Src = "sdk-testing-files/future-search.png", + Signed = false, + ExpiresIn = 3600, + } + ); + + Assert.Contains("ik-t", url); + } + + [Fact] + public void BuildUrl_SignedWithSpecialCharFilename_EncodesAndSigns() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo/", + Src = "sdk-testing-files/हिन्दी.png", + Signed = true, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/demo/sdk-testing-files/%E0%A4%B9%E0%A4%BF%E0%A4%A8%E0%A5%8D%E0%A4%A6%E0%A5%80.png?ik-s=3fff2f31da1f45e007adcdbe95f88c8c330e743c", + url + ); + } + + [Fact] + public void BuildUrl_SignedWithTextOverlaySpecialChars_GeneratesSignedUrl() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo/", + Src = "sdk-testing-files/हिन्दी.png", + Transformation = + [ + new Transformation + { + Overlay = new TextOverlay("हिन्दी") + { + Transformation = + [ + new TextOverlayTransformation + { + FontColor = "red", + FontSize = 32.0, + FontFamily = "sdk-testing-files/Poppins-Regular_Q15GrYWmL.ttf", + }, + ], + }, + }, + ], + Signed = true, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/demo/sdk-testing-files/%E0%A4%B9%E0%A4%BF%E0%A4%A8%E0%A5%8D%E0%A4%A6%E0%A5%80.png?tr=l-text,ie-4KS54KS%2F4KSo4KWN4KSm4KWA,fs-32,ff-sdk-testing-files@@Poppins-Regular_Q15GrYWmL.ttf,co-red,l-end&ik-s=705e41579d368caa6530a4375355325277fcfe5c", + url + ); + } + + [Fact] + public void BuildUrl_SignedWithPathPositionAndSpecialChars_GeneratesSignedUrl() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo/", + Src = "sdk-testing-files/हिन्दी.png", + Transformation = + [ + new Transformation + { + Overlay = new TextOverlay("हिन्दी") + { + Transformation = + [ + new TextOverlayTransformation + { + FontColor = "red", + FontSize = 32.0, + FontFamily = "sdk-testing-files/Poppins-Regular_Q15GrYWmL.ttf", + }, + ], + }, + }, + ], + TransformationPosition = TransformationPosition.Path, + Signed = true, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/demo/tr:l-text,ie-4KS54KS%2F4KSo4KWN4KSm4KWA,fs-32,ff-sdk-testing-files@@Poppins-Regular_Q15GrYWmL.ttf,co-red,l-end/sdk-testing-files/%E0%A4%B9%E0%A4%BF%E0%A4%A8%E0%A5%8D%E0%A4%A6%E0%A5%80.png?ik-s=20958f6126fd67c90653f55a49f2b7bb938d9d1c", + url + ); + } + + [Fact] + public void BuildUrl_SignedWithQueryParameters_IncludesQueryParamsInSignature() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo/", + Src = "sdk-testing-files/future-search.png", + QueryParameters = new System.Collections.Generic.Dictionary + { + ["version"] = "1.0", + ["cache"] = "false", + }, + Signed = true, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/demo/sdk-testing-files/future-search.png?cache=false&version=1.0&ik-s=03767bb6f0898c04e42f65714af65d937c696d66", + url + ); + } + + [Fact] + public void BuildUrl_SignedWithTransformationsAndQueryParams_GeneratesSignedUrl() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo/", + Src = "sdk-testing-files/future-search.png", + Transformation = [new Transformation { Width = 300.0, Height = 200.0 }], + QueryParameters = new System.Collections.Generic.Dictionary + { + ["version"] = "2.0", + }, + Signed = true, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/demo/sdk-testing-files/future-search.png?version=2.0&tr=w-300,h-200&ik-s=601d97a7834b7554f4dabf0d3fc3a219ceeb6b31", + url + ); + } + + [Fact] + public void BuildUrl_SignedFalse_NoSignatureAdded() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo/", + Src = "sdk-testing-files/future-search.png", + Signed = false, + } + ); + + Assert.Equal("https://ik.imagekit.io/demo/sdk-testing-files/future-search.png", url); + Assert.DoesNotContain("ik-s=", url); + Assert.DoesNotContain("ik-t=", url); + } + + [Fact] + public void BuildUrl_SignedWithPathPositionAndQueryParams_GeneratesSignedUrl() + { + var url = _client.Helper.BuildUrl( + new SrcOptions + { + UrlEndpoint = "https://ik.imagekit.io/demo/", + Src = "sdk-testing-files/future-search.png", + Transformation = [new Transformation { Width = 300.0, Height = 200.0 }], + TransformationPosition = TransformationPosition.Path, + QueryParameters = new System.Collections.Generic.Dictionary + { + ["version"] = "2.0", + }, + Signed = true, + } + ); + + Assert.Equal( + "https://ik.imagekit.io/demo/tr:w-300,h-200/sdk-testing-files/future-search.png?version=2.0&ik-s=dd1ee8f83d019bc59fd57a5fc4674a11eb8a3496", + url + ); + } +} diff --git a/src/Imagekit.Tests/Helper/HelperTransformationTest.cs b/src/Imagekit.Tests/Helper/HelperTransformationTest.cs new file mode 100644 index 00000000..8f12584c --- /dev/null +++ b/src/Imagekit.Tests/Helper/HelperTransformationTest.cs @@ -0,0 +1,108 @@ +// Go source: tests/helper_transformation_test.go +// Total test cases in Go: 10 + +using System.Collections.Generic; +using Imagekit; +using Imagekit.Models; + +namespace Imagekit.Tests.Helper; + +public class HelperTransformationTest +{ + private static readonly ImageKitClient _client = new() { PrivateKey = "test-key" }; + + [Fact] + public void BuildTransformationString_NullOrEmpty_ReturnsEmpty() + { + Assert.Equal("", _client.Helper.BuildTransformationString(null!)); + Assert.Equal("", _client.Helper.BuildTransformationString(new List())); + } + + [Fact] + public void BuildTransformationString_WidthOnly_ReturnsCorrectString() + { + var result = _client.Helper.BuildTransformationString( + [new Transformation { Width = 300.0 }] + ); + + Assert.Equal("w-300", result); + } + + [Fact] + public void BuildTransformationString_MultipleParameters_ReturnsCorrectString() + { + var result = _client.Helper.BuildTransformationString( + [new Transformation { Width = 300.0, Height = 200.0 }] + ); + + Assert.Equal("w-300,h-200", result); + } + + [Fact] + public void BuildTransformationString_ChainedTransformations_UsesColonDelimiter() + { + var result = _client.Helper.BuildTransformationString( + [new Transformation { Width = 300.0 }, new Transformation { Height = 200.0 }] + ); + + Assert.Equal("w-300:h-200", result); + } + + [Fact] + public void BuildTransformationString_EmptyTransformationObject_ReturnsEmpty() + { + var result = _client.Helper.BuildTransformationString([new Transformation()]); + + Assert.Equal("", result); + } + + [Fact] + public void BuildTransformationString_WithTextOverlay_ReturnsLayerString() + { + var result = _client.Helper.BuildTransformationString( + [new Transformation { Overlay = new TextOverlay("Hello") }] + ); + + Assert.Equal("l-text,i-Hello,l-end", result); + } + + [Fact] + public void BuildTransformationString_RawParameter_ReturnsRawValue() + { + var result = _client.Helper.BuildTransformationString( + [new Transformation { Raw = "custom-transform-123" }] + ); + + Assert.Equal("custom-transform-123", result); + } + + [Fact] + public void BuildTransformationString_MixedWithRaw_CombinesCorrectly() + { + var result = _client.Helper.BuildTransformationString( + [new Transformation { Width = 300.0, Raw = "custom-param-123" }] + ); + + Assert.Equal("w-300,custom-param-123", result); + } + + [Fact] + public void BuildTransformationString_Quality_ReturnsQualityParam() + { + var result = _client.Helper.BuildTransformationString( + [new Transformation { Quality = 80.0 }] + ); + + Assert.Equal("q-80", result); + } + + [Fact] + public void BuildTransformationString_AspectRatio_ReturnsAspectRatioParam() + { + var result = _client.Helper.BuildTransformationString( + [new Transformation { AspectRatio = "4:3" }] + ); + + Assert.Equal("ar-4:3", result); + } +} diff --git a/src/Imagekit.Tests/Imagekit.Tests.csproj b/src/Imagekit.Tests/Imagekit.Tests.csproj new file mode 100644 index 00000000..d5c95e21 --- /dev/null +++ b/src/Imagekit.Tests/Imagekit.Tests.csproj @@ -0,0 +1,24 @@ + + + true + false + Exe + + + net8.0;net472 + + + $(NoWarn),xUnit1004 + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Imagekit.Tests/Models/AITagTest.cs b/src/Imagekit.Tests/Models/AITagTest.cs new file mode 100644 index 00000000..69b8adb8 --- /dev/null +++ b/src/Imagekit.Tests/Models/AITagTest.cs @@ -0,0 +1,148 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class AITagTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new AITag + { + Confidence = 0, + Name = "name", + Source = "source", + }; + + double expectedConfidence = 0; + string expectedName = "name"; + string expectedSource = "source"; + + Assert.Equal(expectedConfidence, model.Confidence); + Assert.Equal(expectedName, model.Name); + Assert.Equal(expectedSource, model.Source); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new AITag + { + Confidence = 0, + Name = "name", + Source = "source", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new AITag + { + Confidence = 0, + Name = "name", + Source = "source", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + Assert.NotNull(deserialized); + + double expectedConfidence = 0; + string expectedName = "name"; + string expectedSource = "source"; + + Assert.Equal(expectedConfidence, deserialized.Confidence); + Assert.Equal(expectedName, deserialized.Name); + Assert.Equal(expectedSource, deserialized.Source); + } + + [Fact] + public void Validation_Works() + { + var model = new AITag + { + Confidence = 0, + Name = "name", + Source = "source", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new AITag { }; + + Assert.Null(model.Confidence); + Assert.False(model.RawData.ContainsKey("confidence")); + Assert.Null(model.Name); + Assert.False(model.RawData.ContainsKey("name")); + Assert.Null(model.Source); + Assert.False(model.RawData.ContainsKey("source")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new AITag { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new AITag + { + // Null should be interpreted as omitted for these properties + Confidence = null, + Name = null, + Source = null, + }; + + Assert.Null(model.Confidence); + Assert.False(model.RawData.ContainsKey("confidence")); + Assert.Null(model.Name); + Assert.False(model.RawData.ContainsKey("name")); + Assert.Null(model.Source); + Assert.False(model.RawData.ContainsKey("source")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new AITag + { + // Null should be interpreted as omitted for these properties + Confidence = null, + Name = null, + Source = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new AITag + { + Confidence = 0, + Name = "name", + Source = "source", + }; + + AITag copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Accounts/Origins/OriginCreateParamsTest.cs b/src/Imagekit.Tests/Models/Accounts/Origins/OriginCreateParamsTest.cs new file mode 100644 index 00000000..9bba931b --- /dev/null +++ b/src/Imagekit.Tests/Models/Accounts/Origins/OriginCreateParamsTest.cs @@ -0,0 +1,84 @@ +using System; +using Imagekit.Models.Accounts.Origins; + +namespace Imagekit.Tests.Models.Accounts.Origins; + +public class OriginCreateParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new OriginCreateParams + { + OriginRequest = new S3() + { + AccessKey = "AKIATEST123", + Bucket = "test-bucket", + Name = "My S3 Origin", + SecretKey = "secrettest123", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "images", + }, + }; + + OriginRequest expectedOriginRequest = new S3() + { + AccessKey = "AKIATEST123", + Bucket = "test-bucket", + Name = "My S3 Origin", + SecretKey = "secrettest123", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "images", + }; + + Assert.Equal(expectedOriginRequest, parameters.OriginRequest); + } + + [Fact] + public void Url_Works() + { + OriginCreateParams parameters = new() + { + OriginRequest = new S3() + { + AccessKey = "AKIATEST123", + Bucket = "test-bucket", + Name = "My S3 Origin", + SecretKey = "secrettest123", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "images", + }, + }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/accounts/origins"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new OriginCreateParams + { + OriginRequest = new S3() + { + AccessKey = "AKIATEST123", + Bucket = "test-bucket", + Name = "My S3 Origin", + SecretKey = "secrettest123", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "images", + }, + }; + + OriginCreateParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Accounts/Origins/OriginDeleteParamsTest.cs b/src/Imagekit.Tests/Models/Accounts/Origins/OriginDeleteParamsTest.cs new file mode 100644 index 00000000..7f5c61a1 --- /dev/null +++ b/src/Imagekit.Tests/Models/Accounts/Origins/OriginDeleteParamsTest.cs @@ -0,0 +1,39 @@ +using System; +using Imagekit.Models.Accounts.Origins; + +namespace Imagekit.Tests.Models.Accounts.Origins; + +public class OriginDeleteParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new OriginDeleteParams { ID = "id" }; + + string expectedID = "id"; + + Assert.Equal(expectedID, parameters.ID); + } + + [Fact] + public void Url_Works() + { + OriginDeleteParams parameters = new() { ID = "id" }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/accounts/origins/id"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new OriginDeleteParams { ID = "id" }; + + OriginDeleteParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Accounts/Origins/OriginGetParamsTest.cs b/src/Imagekit.Tests/Models/Accounts/Origins/OriginGetParamsTest.cs new file mode 100644 index 00000000..8733b7f3 --- /dev/null +++ b/src/Imagekit.Tests/Models/Accounts/Origins/OriginGetParamsTest.cs @@ -0,0 +1,39 @@ +using System; +using Imagekit.Models.Accounts.Origins; + +namespace Imagekit.Tests.Models.Accounts.Origins; + +public class OriginGetParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new OriginGetParams { ID = "id" }; + + string expectedID = "id"; + + Assert.Equal(expectedID, parameters.ID); + } + + [Fact] + public void Url_Works() + { + OriginGetParams parameters = new() { ID = "id" }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/accounts/origins/id"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new OriginGetParams { ID = "id" }; + + OriginGetParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Accounts/Origins/OriginListParamsTest.cs b/src/Imagekit.Tests/Models/Accounts/Origins/OriginListParamsTest.cs new file mode 100644 index 00000000..a5690465 --- /dev/null +++ b/src/Imagekit.Tests/Models/Accounts/Origins/OriginListParamsTest.cs @@ -0,0 +1 @@ +namespace Imagekit.Tests.Models.Accounts.Origins; diff --git a/src/Imagekit.Tests/Models/Accounts/Origins/OriginRequestTest.cs b/src/Imagekit.Tests/Models/Accounts/Origins/OriginRequestTest.cs new file mode 100644 index 00000000..fb226ecf --- /dev/null +++ b/src/Imagekit.Tests/Models/Accounts/Origins/OriginRequestTest.cs @@ -0,0 +1,1923 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Accounts.Origins; + +namespace Imagekit.Tests.Models.Accounts.Origins; + +public class OriginRequestTest : TestBase +{ + [Fact] + public void S3ValidationWorks() + { + OriginRequest value = new S3() + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "raw-assets", + }; + value.Validate(); + } + + [Fact] + public void S3CompatibleValidationWorks() + { + OriginRequest value = new S3Compatible() + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Endpoint = "https://s3.eu-central-1.wasabisys.com", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "raw-assets", + S3ForcePathStyle = true, + }; + value.Validate(); + } + + [Fact] + public void CloudinaryBackupValidationWorks() + { + OriginRequest value = new CloudinaryBackup() + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "raw-assets", + }; + value.Validate(); + } + + [Fact] + public void WebFolderValidationWorks() + { + OriginRequest value = new WebFolder() + { + BaseUrl = "https://images.example.com/assets", + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + ForwardHostHeaderToOrigin = false, + IncludeCanonicalHeader = false, + }; + value.Validate(); + } + + [Fact] + public void WebProxyValidationWorks() + { + OriginRequest value = new WebProxy() + { + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + }; + value.Validate(); + } + + [Fact] + public void GcsValidationWorks() + { + OriginRequest value = new Gcs() + { + Bucket = "gcs-media", + ClientEmail = "service-account@project.iam.gserviceaccount.com", + Name = "US S3 Storage", + PrivateKey = "-----BEGIN PRIVATE KEY-----\\nMIIEv...", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "products", + }; + value.Validate(); + } + + [Fact] + public void AzureBlobValidationWorks() + { + OriginRequest value = new AzureBlob() + { + AccountName = "account123", + Container = "images", + Name = "US S3 Storage", + SasToken = "?sv=2023-01-03&sr=c&sig=abc123", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "uploads", + }; + value.Validate(); + } + + [Fact] + public void AkeneoPimValidationWorks() + { + OriginRequest value = new AkeneoPim() + { + BaseUrl = "https://akeneo.company.com", + ClientID = "akeneo-client-id", + ClientSecret = "akeneo-client-secret", + Name = "US S3 Storage", + Password = "strongpassword123", + Username = "integration-user", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + }; + value.Validate(); + } + + [Fact] + public void S3SerializationRoundtripWorks() + { + OriginRequest value = new S3() + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "raw-assets", + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void S3CompatibleSerializationRoundtripWorks() + { + OriginRequest value = new S3Compatible() + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Endpoint = "https://s3.eu-central-1.wasabisys.com", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "raw-assets", + S3ForcePathStyle = true, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void CloudinaryBackupSerializationRoundtripWorks() + { + OriginRequest value = new CloudinaryBackup() + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "raw-assets", + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void WebFolderSerializationRoundtripWorks() + { + OriginRequest value = new WebFolder() + { + BaseUrl = "https://images.example.com/assets", + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + ForwardHostHeaderToOrigin = false, + IncludeCanonicalHeader = false, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void WebProxySerializationRoundtripWorks() + { + OriginRequest value = new WebProxy() + { + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void GcsSerializationRoundtripWorks() + { + OriginRequest value = new Gcs() + { + Bucket = "gcs-media", + ClientEmail = "service-account@project.iam.gserviceaccount.com", + Name = "US S3 Storage", + PrivateKey = "-----BEGIN PRIVATE KEY-----\\nMIIEv...", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "products", + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void AzureBlobSerializationRoundtripWorks() + { + OriginRequest value = new AzureBlob() + { + AccountName = "account123", + Container = "images", + Name = "US S3 Storage", + SasToken = "?sv=2023-01-03&sr=c&sig=abc123", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "uploads", + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void AkeneoPimSerializationRoundtripWorks() + { + OriginRequest value = new AkeneoPim() + { + BaseUrl = "https://akeneo.company.com", + ClientID = "akeneo-client-id", + ClientSecret = "akeneo-client-secret", + Name = "US S3 Storage", + Password = "strongpassword123", + Username = "integration-user", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class S3Test : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new S3 + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "raw-assets", + }; + + string expectedAccessKey = "AKIAIOSFODNN7EXAMPLE"; + string expectedBucket = "product-images"; + string expectedName = "US S3 Storage"; + string expectedSecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"; + JsonElement expectedType = JsonSerializer.SerializeToElement("S3"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + bool expectedIncludeCanonicalHeader = false; + string expectedPrefix = "raw-assets"; + + Assert.Equal(expectedAccessKey, model.AccessKey); + Assert.Equal(expectedBucket, model.Bucket); + Assert.Equal(expectedName, model.Name); + Assert.Equal(expectedSecretKey, model.SecretKey); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, model.BaseUrlForCanonicalHeader); + Assert.Equal(expectedIncludeCanonicalHeader, model.IncludeCanonicalHeader); + Assert.Equal(expectedPrefix, model.Prefix); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new S3 + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "raw-assets", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new S3 + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "raw-assets", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + Assert.NotNull(deserialized); + + string expectedAccessKey = "AKIAIOSFODNN7EXAMPLE"; + string expectedBucket = "product-images"; + string expectedName = "US S3 Storage"; + string expectedSecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"; + JsonElement expectedType = JsonSerializer.SerializeToElement("S3"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + bool expectedIncludeCanonicalHeader = false; + string expectedPrefix = "raw-assets"; + + Assert.Equal(expectedAccessKey, deserialized.AccessKey); + Assert.Equal(expectedBucket, deserialized.Bucket); + Assert.Equal(expectedName, deserialized.Name); + Assert.Equal(expectedSecretKey, deserialized.SecretKey); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, deserialized.BaseUrlForCanonicalHeader); + Assert.Equal(expectedIncludeCanonicalHeader, deserialized.IncludeCanonicalHeader); + Assert.Equal(expectedPrefix, deserialized.Prefix); + } + + [Fact] + public void Validation_Works() + { + var model = new S3 + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "raw-assets", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new S3 + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + Assert.Null(model.IncludeCanonicalHeader); + Assert.False(model.RawData.ContainsKey("includeCanonicalHeader")); + Assert.Null(model.Prefix); + Assert.False(model.RawData.ContainsKey("prefix")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new S3 + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new S3 + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + IncludeCanonicalHeader = null, + Prefix = null, + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + Assert.Null(model.IncludeCanonicalHeader); + Assert.False(model.RawData.ContainsKey("includeCanonicalHeader")); + Assert.Null(model.Prefix); + Assert.False(model.RawData.ContainsKey("prefix")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new S3 + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + IncludeCanonicalHeader = null, + Prefix = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new S3 + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "raw-assets", + }; + + S3 copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class S3CompatibleTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new S3Compatible + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Endpoint = "https://s3.eu-central-1.wasabisys.com", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "raw-assets", + S3ForcePathStyle = true, + }; + + string expectedAccessKey = "AKIAIOSFODNN7EXAMPLE"; + string expectedBucket = "product-images"; + string expectedEndpoint = "https://s3.eu-central-1.wasabisys.com"; + string expectedName = "US S3 Storage"; + string expectedSecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"; + JsonElement expectedType = JsonSerializer.SerializeToElement("S3_COMPATIBLE"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + bool expectedIncludeCanonicalHeader = false; + string expectedPrefix = "raw-assets"; + bool expectedS3ForcePathStyle = true; + + Assert.Equal(expectedAccessKey, model.AccessKey); + Assert.Equal(expectedBucket, model.Bucket); + Assert.Equal(expectedEndpoint, model.Endpoint); + Assert.Equal(expectedName, model.Name); + Assert.Equal(expectedSecretKey, model.SecretKey); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, model.BaseUrlForCanonicalHeader); + Assert.Equal(expectedIncludeCanonicalHeader, model.IncludeCanonicalHeader); + Assert.Equal(expectedPrefix, model.Prefix); + Assert.Equal(expectedS3ForcePathStyle, model.S3ForcePathStyle); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new S3Compatible + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Endpoint = "https://s3.eu-central-1.wasabisys.com", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "raw-assets", + S3ForcePathStyle = true, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new S3Compatible + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Endpoint = "https://s3.eu-central-1.wasabisys.com", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "raw-assets", + S3ForcePathStyle = true, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedAccessKey = "AKIAIOSFODNN7EXAMPLE"; + string expectedBucket = "product-images"; + string expectedEndpoint = "https://s3.eu-central-1.wasabisys.com"; + string expectedName = "US S3 Storage"; + string expectedSecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"; + JsonElement expectedType = JsonSerializer.SerializeToElement("S3_COMPATIBLE"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + bool expectedIncludeCanonicalHeader = false; + string expectedPrefix = "raw-assets"; + bool expectedS3ForcePathStyle = true; + + Assert.Equal(expectedAccessKey, deserialized.AccessKey); + Assert.Equal(expectedBucket, deserialized.Bucket); + Assert.Equal(expectedEndpoint, deserialized.Endpoint); + Assert.Equal(expectedName, deserialized.Name); + Assert.Equal(expectedSecretKey, deserialized.SecretKey); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, deserialized.BaseUrlForCanonicalHeader); + Assert.Equal(expectedIncludeCanonicalHeader, deserialized.IncludeCanonicalHeader); + Assert.Equal(expectedPrefix, deserialized.Prefix); + Assert.Equal(expectedS3ForcePathStyle, deserialized.S3ForcePathStyle); + } + + [Fact] + public void Validation_Works() + { + var model = new S3Compatible + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Endpoint = "https://s3.eu-central-1.wasabisys.com", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "raw-assets", + S3ForcePathStyle = true, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new S3Compatible + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Endpoint = "https://s3.eu-central-1.wasabisys.com", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + Assert.Null(model.IncludeCanonicalHeader); + Assert.False(model.RawData.ContainsKey("includeCanonicalHeader")); + Assert.Null(model.Prefix); + Assert.False(model.RawData.ContainsKey("prefix")); + Assert.Null(model.S3ForcePathStyle); + Assert.False(model.RawData.ContainsKey("s3ForcePathStyle")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new S3Compatible + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Endpoint = "https://s3.eu-central-1.wasabisys.com", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new S3Compatible + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Endpoint = "https://s3.eu-central-1.wasabisys.com", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + IncludeCanonicalHeader = null, + Prefix = null, + S3ForcePathStyle = null, + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + Assert.Null(model.IncludeCanonicalHeader); + Assert.False(model.RawData.ContainsKey("includeCanonicalHeader")); + Assert.Null(model.Prefix); + Assert.False(model.RawData.ContainsKey("prefix")); + Assert.Null(model.S3ForcePathStyle); + Assert.False(model.RawData.ContainsKey("s3ForcePathStyle")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new S3Compatible + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Endpoint = "https://s3.eu-central-1.wasabisys.com", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + IncludeCanonicalHeader = null, + Prefix = null, + S3ForcePathStyle = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new S3Compatible + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Endpoint = "https://s3.eu-central-1.wasabisys.com", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "raw-assets", + S3ForcePathStyle = true, + }; + + S3Compatible copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class CloudinaryBackupTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new CloudinaryBackup + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "raw-assets", + }; + + string expectedAccessKey = "AKIAIOSFODNN7EXAMPLE"; + string expectedBucket = "product-images"; + string expectedName = "US S3 Storage"; + string expectedSecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"; + JsonElement expectedType = JsonSerializer.SerializeToElement("CLOUDINARY_BACKUP"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + bool expectedIncludeCanonicalHeader = false; + string expectedPrefix = "raw-assets"; + + Assert.Equal(expectedAccessKey, model.AccessKey); + Assert.Equal(expectedBucket, model.Bucket); + Assert.Equal(expectedName, model.Name); + Assert.Equal(expectedSecretKey, model.SecretKey); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, model.BaseUrlForCanonicalHeader); + Assert.Equal(expectedIncludeCanonicalHeader, model.IncludeCanonicalHeader); + Assert.Equal(expectedPrefix, model.Prefix); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new CloudinaryBackup + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "raw-assets", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new CloudinaryBackup + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "raw-assets", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedAccessKey = "AKIAIOSFODNN7EXAMPLE"; + string expectedBucket = "product-images"; + string expectedName = "US S3 Storage"; + string expectedSecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"; + JsonElement expectedType = JsonSerializer.SerializeToElement("CLOUDINARY_BACKUP"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + bool expectedIncludeCanonicalHeader = false; + string expectedPrefix = "raw-assets"; + + Assert.Equal(expectedAccessKey, deserialized.AccessKey); + Assert.Equal(expectedBucket, deserialized.Bucket); + Assert.Equal(expectedName, deserialized.Name); + Assert.Equal(expectedSecretKey, deserialized.SecretKey); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, deserialized.BaseUrlForCanonicalHeader); + Assert.Equal(expectedIncludeCanonicalHeader, deserialized.IncludeCanonicalHeader); + Assert.Equal(expectedPrefix, deserialized.Prefix); + } + + [Fact] + public void Validation_Works() + { + var model = new CloudinaryBackup + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "raw-assets", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new CloudinaryBackup + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + Assert.Null(model.IncludeCanonicalHeader); + Assert.False(model.RawData.ContainsKey("includeCanonicalHeader")); + Assert.Null(model.Prefix); + Assert.False(model.RawData.ContainsKey("prefix")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new CloudinaryBackup + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new CloudinaryBackup + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + IncludeCanonicalHeader = null, + Prefix = null, + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + Assert.Null(model.IncludeCanonicalHeader); + Assert.False(model.RawData.ContainsKey("includeCanonicalHeader")); + Assert.Null(model.Prefix); + Assert.False(model.RawData.ContainsKey("prefix")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new CloudinaryBackup + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + IncludeCanonicalHeader = null, + Prefix = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new CloudinaryBackup + { + AccessKey = "AKIAIOSFODNN7EXAMPLE", + Bucket = "product-images", + Name = "US S3 Storage", + SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "raw-assets", + }; + + CloudinaryBackup copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class WebFolderTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new WebFolder + { + BaseUrl = "https://images.example.com/assets", + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + ForwardHostHeaderToOrigin = false, + IncludeCanonicalHeader = false, + }; + + string expectedBaseUrl = "https://images.example.com/assets"; + string expectedName = "US S3 Storage"; + JsonElement expectedType = JsonSerializer.SerializeToElement("WEB_FOLDER"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + bool expectedForwardHostHeaderToOrigin = false; + bool expectedIncludeCanonicalHeader = false; + + Assert.Equal(expectedBaseUrl, model.BaseUrl); + Assert.Equal(expectedName, model.Name); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, model.BaseUrlForCanonicalHeader); + Assert.Equal(expectedForwardHostHeaderToOrigin, model.ForwardHostHeaderToOrigin); + Assert.Equal(expectedIncludeCanonicalHeader, model.IncludeCanonicalHeader); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new WebFolder + { + BaseUrl = "https://images.example.com/assets", + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + ForwardHostHeaderToOrigin = false, + IncludeCanonicalHeader = false, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new WebFolder + { + BaseUrl = "https://images.example.com/assets", + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + ForwardHostHeaderToOrigin = false, + IncludeCanonicalHeader = false, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedBaseUrl = "https://images.example.com/assets"; + string expectedName = "US S3 Storage"; + JsonElement expectedType = JsonSerializer.SerializeToElement("WEB_FOLDER"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + bool expectedForwardHostHeaderToOrigin = false; + bool expectedIncludeCanonicalHeader = false; + + Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); + Assert.Equal(expectedName, deserialized.Name); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, deserialized.BaseUrlForCanonicalHeader); + Assert.Equal(expectedForwardHostHeaderToOrigin, deserialized.ForwardHostHeaderToOrigin); + Assert.Equal(expectedIncludeCanonicalHeader, deserialized.IncludeCanonicalHeader); + } + + [Fact] + public void Validation_Works() + { + var model = new WebFolder + { + BaseUrl = "https://images.example.com/assets", + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + ForwardHostHeaderToOrigin = false, + IncludeCanonicalHeader = false, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new WebFolder + { + BaseUrl = "https://images.example.com/assets", + Name = "US S3 Storage", + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + Assert.Null(model.ForwardHostHeaderToOrigin); + Assert.False(model.RawData.ContainsKey("forwardHostHeaderToOrigin")); + Assert.Null(model.IncludeCanonicalHeader); + Assert.False(model.RawData.ContainsKey("includeCanonicalHeader")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new WebFolder + { + BaseUrl = "https://images.example.com/assets", + Name = "US S3 Storage", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new WebFolder + { + BaseUrl = "https://images.example.com/assets", + Name = "US S3 Storage", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + ForwardHostHeaderToOrigin = null, + IncludeCanonicalHeader = null, + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + Assert.Null(model.ForwardHostHeaderToOrigin); + Assert.False(model.RawData.ContainsKey("forwardHostHeaderToOrigin")); + Assert.Null(model.IncludeCanonicalHeader); + Assert.False(model.RawData.ContainsKey("includeCanonicalHeader")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new WebFolder + { + BaseUrl = "https://images.example.com/assets", + Name = "US S3 Storage", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + ForwardHostHeaderToOrigin = null, + IncludeCanonicalHeader = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new WebFolder + { + BaseUrl = "https://images.example.com/assets", + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + ForwardHostHeaderToOrigin = false, + IncludeCanonicalHeader = false, + }; + + WebFolder copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class WebProxyTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new WebProxy + { + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + }; + + string expectedName = "US S3 Storage"; + JsonElement expectedType = JsonSerializer.SerializeToElement("WEB_PROXY"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + bool expectedIncludeCanonicalHeader = false; + + Assert.Equal(expectedName, model.Name); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, model.BaseUrlForCanonicalHeader); + Assert.Equal(expectedIncludeCanonicalHeader, model.IncludeCanonicalHeader); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new WebProxy + { + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new WebProxy + { + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedName = "US S3 Storage"; + JsonElement expectedType = JsonSerializer.SerializeToElement("WEB_PROXY"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + bool expectedIncludeCanonicalHeader = false; + + Assert.Equal(expectedName, deserialized.Name); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, deserialized.BaseUrlForCanonicalHeader); + Assert.Equal(expectedIncludeCanonicalHeader, deserialized.IncludeCanonicalHeader); + } + + [Fact] + public void Validation_Works() + { + var model = new WebProxy + { + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new WebProxy { Name = "US S3 Storage" }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + Assert.Null(model.IncludeCanonicalHeader); + Assert.False(model.RawData.ContainsKey("includeCanonicalHeader")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new WebProxy { Name = "US S3 Storage" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new WebProxy + { + Name = "US S3 Storage", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + IncludeCanonicalHeader = null, + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + Assert.Null(model.IncludeCanonicalHeader); + Assert.False(model.RawData.ContainsKey("includeCanonicalHeader")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new WebProxy + { + Name = "US S3 Storage", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + IncludeCanonicalHeader = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new WebProxy + { + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + }; + + WebProxy copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class GcsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Gcs + { + Bucket = "gcs-media", + ClientEmail = "service-account@project.iam.gserviceaccount.com", + Name = "US S3 Storage", + PrivateKey = "-----BEGIN PRIVATE KEY-----\\nMIIEv...", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "products", + }; + + string expectedBucket = "gcs-media"; + string expectedClientEmail = "service-account@project.iam.gserviceaccount.com"; + string expectedName = "US S3 Storage"; + string expectedPrivateKey = "-----BEGIN PRIVATE KEY-----\\nMIIEv..."; + JsonElement expectedType = JsonSerializer.SerializeToElement("GCS"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + bool expectedIncludeCanonicalHeader = false; + string expectedPrefix = "products"; + + Assert.Equal(expectedBucket, model.Bucket); + Assert.Equal(expectedClientEmail, model.ClientEmail); + Assert.Equal(expectedName, model.Name); + Assert.Equal(expectedPrivateKey, model.PrivateKey); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, model.BaseUrlForCanonicalHeader); + Assert.Equal(expectedIncludeCanonicalHeader, model.IncludeCanonicalHeader); + Assert.Equal(expectedPrefix, model.Prefix); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Gcs + { + Bucket = "gcs-media", + ClientEmail = "service-account@project.iam.gserviceaccount.com", + Name = "US S3 Storage", + PrivateKey = "-----BEGIN PRIVATE KEY-----\\nMIIEv...", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "products", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Gcs + { + Bucket = "gcs-media", + ClientEmail = "service-account@project.iam.gserviceaccount.com", + Name = "US S3 Storage", + PrivateKey = "-----BEGIN PRIVATE KEY-----\\nMIIEv...", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "products", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + Assert.NotNull(deserialized); + + string expectedBucket = "gcs-media"; + string expectedClientEmail = "service-account@project.iam.gserviceaccount.com"; + string expectedName = "US S3 Storage"; + string expectedPrivateKey = "-----BEGIN PRIVATE KEY-----\\nMIIEv..."; + JsonElement expectedType = JsonSerializer.SerializeToElement("GCS"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + bool expectedIncludeCanonicalHeader = false; + string expectedPrefix = "products"; + + Assert.Equal(expectedBucket, deserialized.Bucket); + Assert.Equal(expectedClientEmail, deserialized.ClientEmail); + Assert.Equal(expectedName, deserialized.Name); + Assert.Equal(expectedPrivateKey, deserialized.PrivateKey); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, deserialized.BaseUrlForCanonicalHeader); + Assert.Equal(expectedIncludeCanonicalHeader, deserialized.IncludeCanonicalHeader); + Assert.Equal(expectedPrefix, deserialized.Prefix); + } + + [Fact] + public void Validation_Works() + { + var model = new Gcs + { + Bucket = "gcs-media", + ClientEmail = "service-account@project.iam.gserviceaccount.com", + Name = "US S3 Storage", + PrivateKey = "-----BEGIN PRIVATE KEY-----\\nMIIEv...", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "products", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Gcs + { + Bucket = "gcs-media", + ClientEmail = "service-account@project.iam.gserviceaccount.com", + Name = "US S3 Storage", + PrivateKey = "-----BEGIN PRIVATE KEY-----\\nMIIEv...", + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + Assert.Null(model.IncludeCanonicalHeader); + Assert.False(model.RawData.ContainsKey("includeCanonicalHeader")); + Assert.Null(model.Prefix); + Assert.False(model.RawData.ContainsKey("prefix")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Gcs + { + Bucket = "gcs-media", + ClientEmail = "service-account@project.iam.gserviceaccount.com", + Name = "US S3 Storage", + PrivateKey = "-----BEGIN PRIVATE KEY-----\\nMIIEv...", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Gcs + { + Bucket = "gcs-media", + ClientEmail = "service-account@project.iam.gserviceaccount.com", + Name = "US S3 Storage", + PrivateKey = "-----BEGIN PRIVATE KEY-----\\nMIIEv...", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + IncludeCanonicalHeader = null, + Prefix = null, + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + Assert.Null(model.IncludeCanonicalHeader); + Assert.False(model.RawData.ContainsKey("includeCanonicalHeader")); + Assert.Null(model.Prefix); + Assert.False(model.RawData.ContainsKey("prefix")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Gcs + { + Bucket = "gcs-media", + ClientEmail = "service-account@project.iam.gserviceaccount.com", + Name = "US S3 Storage", + PrivateKey = "-----BEGIN PRIVATE KEY-----\\nMIIEv...", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + IncludeCanonicalHeader = null, + Prefix = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Gcs + { + Bucket = "gcs-media", + ClientEmail = "service-account@project.iam.gserviceaccount.com", + Name = "US S3 Storage", + PrivateKey = "-----BEGIN PRIVATE KEY-----\\nMIIEv...", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "products", + }; + + Gcs copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class AzureBlobTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new AzureBlob + { + AccountName = "account123", + Container = "images", + Name = "US S3 Storage", + SasToken = "?sv=2023-01-03&sr=c&sig=abc123", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "uploads", + }; + + string expectedAccountName = "account123"; + string expectedContainer = "images"; + string expectedName = "US S3 Storage"; + string expectedSasToken = "?sv=2023-01-03&sr=c&sig=abc123"; + JsonElement expectedType = JsonSerializer.SerializeToElement("AZURE_BLOB"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + bool expectedIncludeCanonicalHeader = false; + string expectedPrefix = "uploads"; + + Assert.Equal(expectedAccountName, model.AccountName); + Assert.Equal(expectedContainer, model.Container); + Assert.Equal(expectedName, model.Name); + Assert.Equal(expectedSasToken, model.SasToken); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, model.BaseUrlForCanonicalHeader); + Assert.Equal(expectedIncludeCanonicalHeader, model.IncludeCanonicalHeader); + Assert.Equal(expectedPrefix, model.Prefix); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new AzureBlob + { + AccountName = "account123", + Container = "images", + Name = "US S3 Storage", + SasToken = "?sv=2023-01-03&sr=c&sig=abc123", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "uploads", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new AzureBlob + { + AccountName = "account123", + Container = "images", + Name = "US S3 Storage", + SasToken = "?sv=2023-01-03&sr=c&sig=abc123", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "uploads", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedAccountName = "account123"; + string expectedContainer = "images"; + string expectedName = "US S3 Storage"; + string expectedSasToken = "?sv=2023-01-03&sr=c&sig=abc123"; + JsonElement expectedType = JsonSerializer.SerializeToElement("AZURE_BLOB"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + bool expectedIncludeCanonicalHeader = false; + string expectedPrefix = "uploads"; + + Assert.Equal(expectedAccountName, deserialized.AccountName); + Assert.Equal(expectedContainer, deserialized.Container); + Assert.Equal(expectedName, deserialized.Name); + Assert.Equal(expectedSasToken, deserialized.SasToken); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, deserialized.BaseUrlForCanonicalHeader); + Assert.Equal(expectedIncludeCanonicalHeader, deserialized.IncludeCanonicalHeader); + Assert.Equal(expectedPrefix, deserialized.Prefix); + } + + [Fact] + public void Validation_Works() + { + var model = new AzureBlob + { + AccountName = "account123", + Container = "images", + Name = "US S3 Storage", + SasToken = "?sv=2023-01-03&sr=c&sig=abc123", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "uploads", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new AzureBlob + { + AccountName = "account123", + Container = "images", + Name = "US S3 Storage", + SasToken = "?sv=2023-01-03&sr=c&sig=abc123", + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + Assert.Null(model.IncludeCanonicalHeader); + Assert.False(model.RawData.ContainsKey("includeCanonicalHeader")); + Assert.Null(model.Prefix); + Assert.False(model.RawData.ContainsKey("prefix")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new AzureBlob + { + AccountName = "account123", + Container = "images", + Name = "US S3 Storage", + SasToken = "?sv=2023-01-03&sr=c&sig=abc123", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new AzureBlob + { + AccountName = "account123", + Container = "images", + Name = "US S3 Storage", + SasToken = "?sv=2023-01-03&sr=c&sig=abc123", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + IncludeCanonicalHeader = null, + Prefix = null, + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + Assert.Null(model.IncludeCanonicalHeader); + Assert.False(model.RawData.ContainsKey("includeCanonicalHeader")); + Assert.Null(model.Prefix); + Assert.False(model.RawData.ContainsKey("prefix")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new AzureBlob + { + AccountName = "account123", + Container = "images", + Name = "US S3 Storage", + SasToken = "?sv=2023-01-03&sr=c&sig=abc123", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + IncludeCanonicalHeader = null, + Prefix = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new AzureBlob + { + AccountName = "account123", + Container = "images", + Name = "US S3 Storage", + SasToken = "?sv=2023-01-03&sr=c&sig=abc123", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "uploads", + }; + + AzureBlob copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class AkeneoPimTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new AkeneoPim + { + BaseUrl = "https://akeneo.company.com", + ClientID = "akeneo-client-id", + ClientSecret = "akeneo-client-secret", + Name = "US S3 Storage", + Password = "strongpassword123", + Username = "integration-user", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + }; + + string expectedBaseUrl = "https://akeneo.company.com"; + string expectedClientID = "akeneo-client-id"; + string expectedClientSecret = "akeneo-client-secret"; + string expectedName = "US S3 Storage"; + string expectedPassword = "strongpassword123"; + JsonElement expectedType = JsonSerializer.SerializeToElement("AKENEO_PIM"); + string expectedUsername = "integration-user"; + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + bool expectedIncludeCanonicalHeader = false; + + Assert.Equal(expectedBaseUrl, model.BaseUrl); + Assert.Equal(expectedClientID, model.ClientID); + Assert.Equal(expectedClientSecret, model.ClientSecret); + Assert.Equal(expectedName, model.Name); + Assert.Equal(expectedPassword, model.Password); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedUsername, model.Username); + Assert.Equal(expectedBaseUrlForCanonicalHeader, model.BaseUrlForCanonicalHeader); + Assert.Equal(expectedIncludeCanonicalHeader, model.IncludeCanonicalHeader); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new AkeneoPim + { + BaseUrl = "https://akeneo.company.com", + ClientID = "akeneo-client-id", + ClientSecret = "akeneo-client-secret", + Name = "US S3 Storage", + Password = "strongpassword123", + Username = "integration-user", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new AkeneoPim + { + BaseUrl = "https://akeneo.company.com", + ClientID = "akeneo-client-id", + ClientSecret = "akeneo-client-secret", + Name = "US S3 Storage", + Password = "strongpassword123", + Username = "integration-user", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedBaseUrl = "https://akeneo.company.com"; + string expectedClientID = "akeneo-client-id"; + string expectedClientSecret = "akeneo-client-secret"; + string expectedName = "US S3 Storage"; + string expectedPassword = "strongpassword123"; + JsonElement expectedType = JsonSerializer.SerializeToElement("AKENEO_PIM"); + string expectedUsername = "integration-user"; + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + bool expectedIncludeCanonicalHeader = false; + + Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); + Assert.Equal(expectedClientID, deserialized.ClientID); + Assert.Equal(expectedClientSecret, deserialized.ClientSecret); + Assert.Equal(expectedName, deserialized.Name); + Assert.Equal(expectedPassword, deserialized.Password); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedUsername, deserialized.Username); + Assert.Equal(expectedBaseUrlForCanonicalHeader, deserialized.BaseUrlForCanonicalHeader); + Assert.Equal(expectedIncludeCanonicalHeader, deserialized.IncludeCanonicalHeader); + } + + [Fact] + public void Validation_Works() + { + var model = new AkeneoPim + { + BaseUrl = "https://akeneo.company.com", + ClientID = "akeneo-client-id", + ClientSecret = "akeneo-client-secret", + Name = "US S3 Storage", + Password = "strongpassword123", + Username = "integration-user", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new AkeneoPim + { + BaseUrl = "https://akeneo.company.com", + ClientID = "akeneo-client-id", + ClientSecret = "akeneo-client-secret", + Name = "US S3 Storage", + Password = "strongpassword123", + Username = "integration-user", + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + Assert.Null(model.IncludeCanonicalHeader); + Assert.False(model.RawData.ContainsKey("includeCanonicalHeader")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new AkeneoPim + { + BaseUrl = "https://akeneo.company.com", + ClientID = "akeneo-client-id", + ClientSecret = "akeneo-client-secret", + Name = "US S3 Storage", + Password = "strongpassword123", + Username = "integration-user", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new AkeneoPim + { + BaseUrl = "https://akeneo.company.com", + ClientID = "akeneo-client-id", + ClientSecret = "akeneo-client-secret", + Name = "US S3 Storage", + Password = "strongpassword123", + Username = "integration-user", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + IncludeCanonicalHeader = null, + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + Assert.Null(model.IncludeCanonicalHeader); + Assert.False(model.RawData.ContainsKey("includeCanonicalHeader")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new AkeneoPim + { + BaseUrl = "https://akeneo.company.com", + ClientID = "akeneo-client-id", + ClientSecret = "akeneo-client-secret", + Name = "US S3 Storage", + Password = "strongpassword123", + Username = "integration-user", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + IncludeCanonicalHeader = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new AkeneoPim + { + BaseUrl = "https://akeneo.company.com", + ClientID = "akeneo-client-id", + ClientSecret = "akeneo-client-secret", + Name = "US S3 Storage", + Password = "strongpassword123", + Username = "integration-user", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + }; + + AkeneoPim copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Accounts/Origins/OriginResponseTest.cs b/src/Imagekit.Tests/Models/Accounts/Origins/OriginResponseTest.cs new file mode 100644 index 00000000..8f9e7720 --- /dev/null +++ b/src/Imagekit.Tests/Models/Accounts/Origins/OriginResponseTest.cs @@ -0,0 +1,1863 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Accounts.Origins; + +namespace Imagekit.Tests.Models.Accounts.Origins; + +public class OriginResponseTest : TestBase +{ + [Fact] + public void S3ValidationWorks() + { + OriginResponse value = new OriginResponseS3() + { + ID = "id", + Bucket = "product-images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + value.Validate(); + } + + [Fact] + public void S3CompatibleValidationWorks() + { + OriginResponse value = new OriginResponseS3Compatible() + { + ID = "id", + Bucket = "product-images", + Endpoint = "https://s3.eu-central-1.wasabisys.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + S3ForcePathStyle = true, + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + value.Validate(); + } + + [Fact] + public void CloudinaryBackupValidationWorks() + { + OriginResponse value = new OriginResponseCloudinaryBackup() + { + ID = "id", + Bucket = "product-images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + value.Validate(); + } + + [Fact] + public void WebFolderValidationWorks() + { + OriginResponse value = new OriginResponseWebFolder() + { + ID = "id", + BaseUrl = "https://images.example.com/assets", + ForwardHostHeaderToOrigin = false, + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + value.Validate(); + } + + [Fact] + public void WebProxyValidationWorks() + { + OriginResponse value = new OriginResponseWebProxy() + { + ID = "id", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + value.Validate(); + } + + [Fact] + public void GcsValidationWorks() + { + OriginResponse value = new OriginResponseGcs() + { + ID = "id", + Bucket = "gcs-media", + ClientEmail = "service-account@project.iam.gserviceaccount.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "products", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + value.Validate(); + } + + [Fact] + public void AzureBlobValidationWorks() + { + OriginResponse value = new OriginResponseAzureBlob() + { + ID = "id", + AccountName = "account123", + Container = "images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "uploads", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + value.Validate(); + } + + [Fact] + public void AkeneoPimValidationWorks() + { + OriginResponse value = new OriginResponseAkeneoPim() + { + ID = "id", + BaseUrl = "https://akeneo.company.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + value.Validate(); + } + + [Fact] + public void S3SerializationRoundtripWorks() + { + OriginResponse value = new OriginResponseS3() + { + ID = "id", + Bucket = "product-images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void S3CompatibleSerializationRoundtripWorks() + { + OriginResponse value = new OriginResponseS3Compatible() + { + ID = "id", + Bucket = "product-images", + Endpoint = "https://s3.eu-central-1.wasabisys.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + S3ForcePathStyle = true, + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void CloudinaryBackupSerializationRoundtripWorks() + { + OriginResponse value = new OriginResponseCloudinaryBackup() + { + ID = "id", + Bucket = "product-images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void WebFolderSerializationRoundtripWorks() + { + OriginResponse value = new OriginResponseWebFolder() + { + ID = "id", + BaseUrl = "https://images.example.com/assets", + ForwardHostHeaderToOrigin = false, + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void WebProxySerializationRoundtripWorks() + { + OriginResponse value = new OriginResponseWebProxy() + { + ID = "id", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void GcsSerializationRoundtripWorks() + { + OriginResponse value = new OriginResponseGcs() + { + ID = "id", + Bucket = "gcs-media", + ClientEmail = "service-account@project.iam.gserviceaccount.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "products", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void AzureBlobSerializationRoundtripWorks() + { + OriginResponse value = new OriginResponseAzureBlob() + { + ID = "id", + AccountName = "account123", + Container = "images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "uploads", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void AkeneoPimSerializationRoundtripWorks() + { + OriginResponse value = new OriginResponseAkeneoPim() + { + ID = "id", + BaseUrl = "https://akeneo.company.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class OriginResponseS3Test : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new OriginResponseS3 + { + ID = "id", + Bucket = "product-images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string expectedID = "id"; + string expectedBucket = "product-images"; + bool expectedIncludeCanonicalHeader = false; + string expectedName = "US S3 Storage"; + string expectedPrefix = "raw-assets"; + JsonElement expectedType = JsonSerializer.SerializeToElement("S3"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedBucket, model.Bucket); + Assert.Equal(expectedIncludeCanonicalHeader, model.IncludeCanonicalHeader); + Assert.Equal(expectedName, model.Name); + Assert.Equal(expectedPrefix, model.Prefix); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, model.BaseUrlForCanonicalHeader); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new OriginResponseS3 + { + ID = "id", + Bucket = "product-images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new OriginResponseS3 + { + ID = "id", + Bucket = "product-images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedBucket = "product-images"; + bool expectedIncludeCanonicalHeader = false; + string expectedName = "US S3 Storage"; + string expectedPrefix = "raw-assets"; + JsonElement expectedType = JsonSerializer.SerializeToElement("S3"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedBucket, deserialized.Bucket); + Assert.Equal(expectedIncludeCanonicalHeader, deserialized.IncludeCanonicalHeader); + Assert.Equal(expectedName, deserialized.Name); + Assert.Equal(expectedPrefix, deserialized.Prefix); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, deserialized.BaseUrlForCanonicalHeader); + } + + [Fact] + public void Validation_Works() + { + var model = new OriginResponseS3 + { + ID = "id", + Bucket = "product-images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new OriginResponseS3 + { + ID = "id", + Bucket = "product-images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new OriginResponseS3 + { + ID = "id", + Bucket = "product-images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new OriginResponseS3 + { + ID = "id", + Bucket = "product-images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new OriginResponseS3 + { + ID = "id", + Bucket = "product-images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new OriginResponseS3 + { + ID = "id", + Bucket = "product-images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + OriginResponseS3 copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class OriginResponseS3CompatibleTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new OriginResponseS3Compatible + { + ID = "id", + Bucket = "product-images", + Endpoint = "https://s3.eu-central-1.wasabisys.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + S3ForcePathStyle = true, + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string expectedID = "id"; + string expectedBucket = "product-images"; + string expectedEndpoint = "https://s3.eu-central-1.wasabisys.com"; + bool expectedIncludeCanonicalHeader = false; + string expectedName = "US S3 Storage"; + string expectedPrefix = "raw-assets"; + bool expectedS3ForcePathStyle = true; + JsonElement expectedType = JsonSerializer.SerializeToElement("S3_COMPATIBLE"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedBucket, model.Bucket); + Assert.Equal(expectedEndpoint, model.Endpoint); + Assert.Equal(expectedIncludeCanonicalHeader, model.IncludeCanonicalHeader); + Assert.Equal(expectedName, model.Name); + Assert.Equal(expectedPrefix, model.Prefix); + Assert.Equal(expectedS3ForcePathStyle, model.S3ForcePathStyle); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, model.BaseUrlForCanonicalHeader); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new OriginResponseS3Compatible + { + ID = "id", + Bucket = "product-images", + Endpoint = "https://s3.eu-central-1.wasabisys.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + S3ForcePathStyle = true, + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new OriginResponseS3Compatible + { + ID = "id", + Bucket = "product-images", + Endpoint = "https://s3.eu-central-1.wasabisys.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + S3ForcePathStyle = true, + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedBucket = "product-images"; + string expectedEndpoint = "https://s3.eu-central-1.wasabisys.com"; + bool expectedIncludeCanonicalHeader = false; + string expectedName = "US S3 Storage"; + string expectedPrefix = "raw-assets"; + bool expectedS3ForcePathStyle = true; + JsonElement expectedType = JsonSerializer.SerializeToElement("S3_COMPATIBLE"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedBucket, deserialized.Bucket); + Assert.Equal(expectedEndpoint, deserialized.Endpoint); + Assert.Equal(expectedIncludeCanonicalHeader, deserialized.IncludeCanonicalHeader); + Assert.Equal(expectedName, deserialized.Name); + Assert.Equal(expectedPrefix, deserialized.Prefix); + Assert.Equal(expectedS3ForcePathStyle, deserialized.S3ForcePathStyle); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, deserialized.BaseUrlForCanonicalHeader); + } + + [Fact] + public void Validation_Works() + { + var model = new OriginResponseS3Compatible + { + ID = "id", + Bucket = "product-images", + Endpoint = "https://s3.eu-central-1.wasabisys.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + S3ForcePathStyle = true, + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new OriginResponseS3Compatible + { + ID = "id", + Bucket = "product-images", + Endpoint = "https://s3.eu-central-1.wasabisys.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + S3ForcePathStyle = true, + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new OriginResponseS3Compatible + { + ID = "id", + Bucket = "product-images", + Endpoint = "https://s3.eu-central-1.wasabisys.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + S3ForcePathStyle = true, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new OriginResponseS3Compatible + { + ID = "id", + Bucket = "product-images", + Endpoint = "https://s3.eu-central-1.wasabisys.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + S3ForcePathStyle = true, + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new OriginResponseS3Compatible + { + ID = "id", + Bucket = "product-images", + Endpoint = "https://s3.eu-central-1.wasabisys.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + S3ForcePathStyle = true, + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new OriginResponseS3Compatible + { + ID = "id", + Bucket = "product-images", + Endpoint = "https://s3.eu-central-1.wasabisys.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + S3ForcePathStyle = true, + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + OriginResponseS3Compatible copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class OriginResponseCloudinaryBackupTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new OriginResponseCloudinaryBackup + { + ID = "id", + Bucket = "product-images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string expectedID = "id"; + string expectedBucket = "product-images"; + bool expectedIncludeCanonicalHeader = false; + string expectedName = "US S3 Storage"; + string expectedPrefix = "raw-assets"; + JsonElement expectedType = JsonSerializer.SerializeToElement("CLOUDINARY_BACKUP"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedBucket, model.Bucket); + Assert.Equal(expectedIncludeCanonicalHeader, model.IncludeCanonicalHeader); + Assert.Equal(expectedName, model.Name); + Assert.Equal(expectedPrefix, model.Prefix); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, model.BaseUrlForCanonicalHeader); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new OriginResponseCloudinaryBackup + { + ID = "id", + Bucket = "product-images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new OriginResponseCloudinaryBackup + { + ID = "id", + Bucket = "product-images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedBucket = "product-images"; + bool expectedIncludeCanonicalHeader = false; + string expectedName = "US S3 Storage"; + string expectedPrefix = "raw-assets"; + JsonElement expectedType = JsonSerializer.SerializeToElement("CLOUDINARY_BACKUP"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedBucket, deserialized.Bucket); + Assert.Equal(expectedIncludeCanonicalHeader, deserialized.IncludeCanonicalHeader); + Assert.Equal(expectedName, deserialized.Name); + Assert.Equal(expectedPrefix, deserialized.Prefix); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, deserialized.BaseUrlForCanonicalHeader); + } + + [Fact] + public void Validation_Works() + { + var model = new OriginResponseCloudinaryBackup + { + ID = "id", + Bucket = "product-images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new OriginResponseCloudinaryBackup + { + ID = "id", + Bucket = "product-images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new OriginResponseCloudinaryBackup + { + ID = "id", + Bucket = "product-images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new OriginResponseCloudinaryBackup + { + ID = "id", + Bucket = "product-images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new OriginResponseCloudinaryBackup + { + ID = "id", + Bucket = "product-images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new OriginResponseCloudinaryBackup + { + ID = "id", + Bucket = "product-images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "raw-assets", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + OriginResponseCloudinaryBackup copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class OriginResponseWebFolderTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new OriginResponseWebFolder + { + ID = "id", + BaseUrl = "https://images.example.com/assets", + ForwardHostHeaderToOrigin = false, + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string expectedID = "id"; + string expectedBaseUrl = "https://images.example.com/assets"; + bool expectedForwardHostHeaderToOrigin = false; + bool expectedIncludeCanonicalHeader = false; + string expectedName = "US S3 Storage"; + JsonElement expectedType = JsonSerializer.SerializeToElement("WEB_FOLDER"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedBaseUrl, model.BaseUrl); + Assert.Equal(expectedForwardHostHeaderToOrigin, model.ForwardHostHeaderToOrigin); + Assert.Equal(expectedIncludeCanonicalHeader, model.IncludeCanonicalHeader); + Assert.Equal(expectedName, model.Name); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, model.BaseUrlForCanonicalHeader); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new OriginResponseWebFolder + { + ID = "id", + BaseUrl = "https://images.example.com/assets", + ForwardHostHeaderToOrigin = false, + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new OriginResponseWebFolder + { + ID = "id", + BaseUrl = "https://images.example.com/assets", + ForwardHostHeaderToOrigin = false, + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedBaseUrl = "https://images.example.com/assets"; + bool expectedForwardHostHeaderToOrigin = false; + bool expectedIncludeCanonicalHeader = false; + string expectedName = "US S3 Storage"; + JsonElement expectedType = JsonSerializer.SerializeToElement("WEB_FOLDER"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); + Assert.Equal(expectedForwardHostHeaderToOrigin, deserialized.ForwardHostHeaderToOrigin); + Assert.Equal(expectedIncludeCanonicalHeader, deserialized.IncludeCanonicalHeader); + Assert.Equal(expectedName, deserialized.Name); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, deserialized.BaseUrlForCanonicalHeader); + } + + [Fact] + public void Validation_Works() + { + var model = new OriginResponseWebFolder + { + ID = "id", + BaseUrl = "https://images.example.com/assets", + ForwardHostHeaderToOrigin = false, + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new OriginResponseWebFolder + { + ID = "id", + BaseUrl = "https://images.example.com/assets", + ForwardHostHeaderToOrigin = false, + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new OriginResponseWebFolder + { + ID = "id", + BaseUrl = "https://images.example.com/assets", + ForwardHostHeaderToOrigin = false, + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new OriginResponseWebFolder + { + ID = "id", + BaseUrl = "https://images.example.com/assets", + ForwardHostHeaderToOrigin = false, + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new OriginResponseWebFolder + { + ID = "id", + BaseUrl = "https://images.example.com/assets", + ForwardHostHeaderToOrigin = false, + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new OriginResponseWebFolder + { + ID = "id", + BaseUrl = "https://images.example.com/assets", + ForwardHostHeaderToOrigin = false, + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + OriginResponseWebFolder copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class OriginResponseWebProxyTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new OriginResponseWebProxy + { + ID = "id", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string expectedID = "id"; + bool expectedIncludeCanonicalHeader = false; + string expectedName = "US S3 Storage"; + JsonElement expectedType = JsonSerializer.SerializeToElement("WEB_PROXY"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedIncludeCanonicalHeader, model.IncludeCanonicalHeader); + Assert.Equal(expectedName, model.Name); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, model.BaseUrlForCanonicalHeader); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new OriginResponseWebProxy + { + ID = "id", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new OriginResponseWebProxy + { + ID = "id", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + bool expectedIncludeCanonicalHeader = false; + string expectedName = "US S3 Storage"; + JsonElement expectedType = JsonSerializer.SerializeToElement("WEB_PROXY"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedIncludeCanonicalHeader, deserialized.IncludeCanonicalHeader); + Assert.Equal(expectedName, deserialized.Name); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, deserialized.BaseUrlForCanonicalHeader); + } + + [Fact] + public void Validation_Works() + { + var model = new OriginResponseWebProxy + { + ID = "id", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new OriginResponseWebProxy + { + ID = "id", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new OriginResponseWebProxy + { + ID = "id", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new OriginResponseWebProxy + { + ID = "id", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new OriginResponseWebProxy + { + ID = "id", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new OriginResponseWebProxy + { + ID = "id", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + OriginResponseWebProxy copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class OriginResponseGcsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new OriginResponseGcs + { + ID = "id", + Bucket = "gcs-media", + ClientEmail = "service-account@project.iam.gserviceaccount.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "products", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string expectedID = "id"; + string expectedBucket = "gcs-media"; + string expectedClientEmail = "service-account@project.iam.gserviceaccount.com"; + bool expectedIncludeCanonicalHeader = false; + string expectedName = "US S3 Storage"; + string expectedPrefix = "products"; + JsonElement expectedType = JsonSerializer.SerializeToElement("GCS"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedBucket, model.Bucket); + Assert.Equal(expectedClientEmail, model.ClientEmail); + Assert.Equal(expectedIncludeCanonicalHeader, model.IncludeCanonicalHeader); + Assert.Equal(expectedName, model.Name); + Assert.Equal(expectedPrefix, model.Prefix); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, model.BaseUrlForCanonicalHeader); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new OriginResponseGcs + { + ID = "id", + Bucket = "gcs-media", + ClientEmail = "service-account@project.iam.gserviceaccount.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "products", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new OriginResponseGcs + { + ID = "id", + Bucket = "gcs-media", + ClientEmail = "service-account@project.iam.gserviceaccount.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "products", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedBucket = "gcs-media"; + string expectedClientEmail = "service-account@project.iam.gserviceaccount.com"; + bool expectedIncludeCanonicalHeader = false; + string expectedName = "US S3 Storage"; + string expectedPrefix = "products"; + JsonElement expectedType = JsonSerializer.SerializeToElement("GCS"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedBucket, deserialized.Bucket); + Assert.Equal(expectedClientEmail, deserialized.ClientEmail); + Assert.Equal(expectedIncludeCanonicalHeader, deserialized.IncludeCanonicalHeader); + Assert.Equal(expectedName, deserialized.Name); + Assert.Equal(expectedPrefix, deserialized.Prefix); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, deserialized.BaseUrlForCanonicalHeader); + } + + [Fact] + public void Validation_Works() + { + var model = new OriginResponseGcs + { + ID = "id", + Bucket = "gcs-media", + ClientEmail = "service-account@project.iam.gserviceaccount.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "products", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new OriginResponseGcs + { + ID = "id", + Bucket = "gcs-media", + ClientEmail = "service-account@project.iam.gserviceaccount.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "products", + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new OriginResponseGcs + { + ID = "id", + Bucket = "gcs-media", + ClientEmail = "service-account@project.iam.gserviceaccount.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "products", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new OriginResponseGcs + { + ID = "id", + Bucket = "gcs-media", + ClientEmail = "service-account@project.iam.gserviceaccount.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "products", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new OriginResponseGcs + { + ID = "id", + Bucket = "gcs-media", + ClientEmail = "service-account@project.iam.gserviceaccount.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "products", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new OriginResponseGcs + { + ID = "id", + Bucket = "gcs-media", + ClientEmail = "service-account@project.iam.gserviceaccount.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "products", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + OriginResponseGcs copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class OriginResponseAzureBlobTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new OriginResponseAzureBlob + { + ID = "id", + AccountName = "account123", + Container = "images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "uploads", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string expectedID = "id"; + string expectedAccountName = "account123"; + string expectedContainer = "images"; + bool expectedIncludeCanonicalHeader = false; + string expectedName = "US S3 Storage"; + string expectedPrefix = "uploads"; + JsonElement expectedType = JsonSerializer.SerializeToElement("AZURE_BLOB"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedAccountName, model.AccountName); + Assert.Equal(expectedContainer, model.Container); + Assert.Equal(expectedIncludeCanonicalHeader, model.IncludeCanonicalHeader); + Assert.Equal(expectedName, model.Name); + Assert.Equal(expectedPrefix, model.Prefix); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, model.BaseUrlForCanonicalHeader); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new OriginResponseAzureBlob + { + ID = "id", + AccountName = "account123", + Container = "images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "uploads", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new OriginResponseAzureBlob + { + ID = "id", + AccountName = "account123", + Container = "images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "uploads", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedAccountName = "account123"; + string expectedContainer = "images"; + bool expectedIncludeCanonicalHeader = false; + string expectedName = "US S3 Storage"; + string expectedPrefix = "uploads"; + JsonElement expectedType = JsonSerializer.SerializeToElement("AZURE_BLOB"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedAccountName, deserialized.AccountName); + Assert.Equal(expectedContainer, deserialized.Container); + Assert.Equal(expectedIncludeCanonicalHeader, deserialized.IncludeCanonicalHeader); + Assert.Equal(expectedName, deserialized.Name); + Assert.Equal(expectedPrefix, deserialized.Prefix); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, deserialized.BaseUrlForCanonicalHeader); + } + + [Fact] + public void Validation_Works() + { + var model = new OriginResponseAzureBlob + { + ID = "id", + AccountName = "account123", + Container = "images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "uploads", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new OriginResponseAzureBlob + { + ID = "id", + AccountName = "account123", + Container = "images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "uploads", + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new OriginResponseAzureBlob + { + ID = "id", + AccountName = "account123", + Container = "images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "uploads", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new OriginResponseAzureBlob + { + ID = "id", + AccountName = "account123", + Container = "images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "uploads", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new OriginResponseAzureBlob + { + ID = "id", + AccountName = "account123", + Container = "images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "uploads", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new OriginResponseAzureBlob + { + ID = "id", + AccountName = "account123", + Container = "images", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + Prefix = "uploads", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + OriginResponseAzureBlob copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class OriginResponseAkeneoPimTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new OriginResponseAkeneoPim + { + ID = "id", + BaseUrl = "https://akeneo.company.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string expectedID = "id"; + string expectedBaseUrl = "https://akeneo.company.com"; + bool expectedIncludeCanonicalHeader = false; + string expectedName = "US S3 Storage"; + JsonElement expectedType = JsonSerializer.SerializeToElement("AKENEO_PIM"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedBaseUrl, model.BaseUrl); + Assert.Equal(expectedIncludeCanonicalHeader, model.IncludeCanonicalHeader); + Assert.Equal(expectedName, model.Name); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, model.BaseUrlForCanonicalHeader); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new OriginResponseAkeneoPim + { + ID = "id", + BaseUrl = "https://akeneo.company.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new OriginResponseAkeneoPim + { + ID = "id", + BaseUrl = "https://akeneo.company.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedBaseUrl = "https://akeneo.company.com"; + bool expectedIncludeCanonicalHeader = false; + string expectedName = "US S3 Storage"; + JsonElement expectedType = JsonSerializer.SerializeToElement("AKENEO_PIM"); + string expectedBaseUrlForCanonicalHeader = "https://cdn.example.com"; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); + Assert.Equal(expectedIncludeCanonicalHeader, deserialized.IncludeCanonicalHeader); + Assert.Equal(expectedName, deserialized.Name); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedBaseUrlForCanonicalHeader, deserialized.BaseUrlForCanonicalHeader); + } + + [Fact] + public void Validation_Works() + { + var model = new OriginResponseAkeneoPim + { + ID = "id", + BaseUrl = "https://akeneo.company.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new OriginResponseAkeneoPim + { + ID = "id", + BaseUrl = "https://akeneo.company.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new OriginResponseAkeneoPim + { + ID = "id", + BaseUrl = "https://akeneo.company.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new OriginResponseAkeneoPim + { + ID = "id", + BaseUrl = "https://akeneo.company.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + }; + + Assert.Null(model.BaseUrlForCanonicalHeader); + Assert.False(model.RawData.ContainsKey("baseUrlForCanonicalHeader")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new OriginResponseAkeneoPim + { + ID = "id", + BaseUrl = "https://akeneo.company.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + + // Null should be interpreted as omitted for these properties + BaseUrlForCanonicalHeader = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new OriginResponseAkeneoPim + { + ID = "id", + BaseUrl = "https://akeneo.company.com", + IncludeCanonicalHeader = false, + Name = "US S3 Storage", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + }; + + OriginResponseAkeneoPim copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Accounts/Origins/OriginUpdateParamsTest.cs b/src/Imagekit.Tests/Models/Accounts/Origins/OriginUpdateParamsTest.cs new file mode 100644 index 00000000..8a287a05 --- /dev/null +++ b/src/Imagekit.Tests/Models/Accounts/Origins/OriginUpdateParamsTest.cs @@ -0,0 +1,89 @@ +using System; +using Imagekit.Models.Accounts.Origins; + +namespace Imagekit.Tests.Models.Accounts.Origins; + +public class OriginUpdateParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new OriginUpdateParams + { + ID = "id", + OriginRequest = new S3() + { + AccessKey = "AKIATEST123", + Bucket = "test-bucket", + Name = "My S3 Origin", + SecretKey = "secrettest123", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "images", + }, + }; + + string expectedID = "id"; + OriginRequest expectedOriginRequest = new S3() + { + AccessKey = "AKIATEST123", + Bucket = "test-bucket", + Name = "My S3 Origin", + SecretKey = "secrettest123", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "images", + }; + + Assert.Equal(expectedID, parameters.ID); + Assert.Equal(expectedOriginRequest, parameters.OriginRequest); + } + + [Fact] + public void Url_Works() + { + OriginUpdateParams parameters = new() + { + ID = "id", + OriginRequest = new S3() + { + AccessKey = "AKIATEST123", + Bucket = "test-bucket", + Name = "My S3 Origin", + SecretKey = "secrettest123", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "images", + }, + }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/accounts/origins/id"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new OriginUpdateParams + { + ID = "id", + OriginRequest = new S3() + { + AccessKey = "AKIATEST123", + Bucket = "test-bucket", + Name = "My S3 Origin", + SecretKey = "secrettest123", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "images", + }, + }; + + OriginUpdateParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Accounts/UrlEndpoints/UrlEndpointCreateParamsTest.cs b/src/Imagekit.Tests/Models/Accounts/UrlEndpoints/UrlEndpointCreateParamsTest.cs new file mode 100644 index 00000000..10b2002d --- /dev/null +++ b/src/Imagekit.Tests/Models/Accounts/UrlEndpoints/UrlEndpointCreateParamsTest.cs @@ -0,0 +1,435 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Accounts.UrlEndpoints; + +namespace Imagekit.Tests.Models.Accounts.UrlEndpoints; + +public class UrlEndpointCreateParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new UrlEndpointCreateParams + { + Description = "My custom URL endpoint", + Origins = ["origin-id-1"], + UrlPrefix = "product-images", + UrlRewriter = new Cloudinary() { PreserveAssetDeliveryTypes = true }, + }; + + string expectedDescription = "My custom URL endpoint"; + List expectedOrigins = ["origin-id-1"]; + string expectedUrlPrefix = "product-images"; + UrlRewriter expectedUrlRewriter = new Cloudinary() { PreserveAssetDeliveryTypes = true }; + + Assert.Equal(expectedDescription, parameters.Description); + Assert.NotNull(parameters.Origins); + Assert.Equal(expectedOrigins.Count, parameters.Origins.Count); + for (int i = 0; i < expectedOrigins.Count; i++) + { + Assert.Equal(expectedOrigins[i], parameters.Origins[i]); + } + Assert.Equal(expectedUrlPrefix, parameters.UrlPrefix); + Assert.Equal(expectedUrlRewriter, parameters.UrlRewriter); + } + + [Fact] + public void OptionalNonNullableParamsUnsetAreNotSet_Works() + { + var parameters = new UrlEndpointCreateParams { Description = "My custom URL endpoint" }; + + Assert.Null(parameters.Origins); + Assert.False(parameters.RawBodyData.ContainsKey("origins")); + Assert.Null(parameters.UrlPrefix); + Assert.False(parameters.RawBodyData.ContainsKey("urlPrefix")); + Assert.Null(parameters.UrlRewriter); + Assert.False(parameters.RawBodyData.ContainsKey("urlRewriter")); + } + + [Fact] + public void OptionalNonNullableParamsSetToNullAreNotSet_Works() + { + var parameters = new UrlEndpointCreateParams + { + Description = "My custom URL endpoint", + + // Null should be interpreted as omitted for these properties + Origins = null, + UrlPrefix = null, + UrlRewriter = null, + }; + + Assert.Null(parameters.Origins); + Assert.False(parameters.RawBodyData.ContainsKey("origins")); + Assert.Null(parameters.UrlPrefix); + Assert.False(parameters.RawBodyData.ContainsKey("urlPrefix")); + Assert.Null(parameters.UrlRewriter); + Assert.False(parameters.RawBodyData.ContainsKey("urlRewriter")); + } + + [Fact] + public void Url_Works() + { + UrlEndpointCreateParams parameters = new() { Description = "My custom URL endpoint" }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/accounts/url-endpoints"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new UrlEndpointCreateParams + { + Description = "My custom URL endpoint", + Origins = ["origin-id-1"], + UrlPrefix = "product-images", + UrlRewriter = new Cloudinary() { PreserveAssetDeliveryTypes = true }, + }; + + UrlEndpointCreateParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} + +public class UrlRewriterTest : TestBase +{ + [Fact] + public void CloudinaryValidationWorks() + { + UrlRewriter value = new Cloudinary() { PreserveAssetDeliveryTypes = true }; + value.Validate(); + } + + [Fact] + public void ImgixValidationWorks() + { + UrlRewriter value = new Imgix(); + value.Validate(); + } + + [Fact] + public void AkamaiValidationWorks() + { + UrlRewriter value = new Akamai(); + value.Validate(); + } + + [Fact] + public void CloudinarySerializationRoundtripWorks() + { + UrlRewriter value = new Cloudinary() { PreserveAssetDeliveryTypes = true }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void ImgixSerializationRoundtripWorks() + { + UrlRewriter value = new Imgix(); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void AkamaiSerializationRoundtripWorks() + { + UrlRewriter value = new Akamai(); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class CloudinaryTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Cloudinary { PreserveAssetDeliveryTypes = true }; + + JsonElement expectedType = JsonSerializer.SerializeToElement("CLOUDINARY"); + bool expectedPreserveAssetDeliveryTypes = true; + + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedPreserveAssetDeliveryTypes, model.PreserveAssetDeliveryTypes); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Cloudinary { PreserveAssetDeliveryTypes = true }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Cloudinary { PreserveAssetDeliveryTypes = true }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + JsonElement expectedType = JsonSerializer.SerializeToElement("CLOUDINARY"); + bool expectedPreserveAssetDeliveryTypes = true; + + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedPreserveAssetDeliveryTypes, deserialized.PreserveAssetDeliveryTypes); + } + + [Fact] + public void Validation_Works() + { + var model = new Cloudinary { PreserveAssetDeliveryTypes = true }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Cloudinary { }; + + Assert.Null(model.PreserveAssetDeliveryTypes); + Assert.False(model.RawData.ContainsKey("preserveAssetDeliveryTypes")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Cloudinary { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Cloudinary + { + // Null should be interpreted as omitted for these properties + PreserveAssetDeliveryTypes = null, + }; + + Assert.Null(model.PreserveAssetDeliveryTypes); + Assert.False(model.RawData.ContainsKey("preserveAssetDeliveryTypes")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Cloudinary + { + // Null should be interpreted as omitted for these properties + PreserveAssetDeliveryTypes = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Cloudinary { PreserveAssetDeliveryTypes = true }; + + Cloudinary copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ImgixTest : TestBase +{ + [Fact] + public void DefaultValidation_Works() + { + var constant = new Imgix(); + constant.Validate(); + } + + [Fact] + public void ValidConstantValidation_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.Deserialize( + """ + { + "type": "IMGIX" + } + """ + ), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + constant.Validate(); + } + + [Fact] + public void InvalidConstantValidationThrows_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + Assert.Throws(() => constant.Validate()); + } + + [Fact] + public void DefaultRoundtrip_Works() + { + var constant = new Imgix(); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void ValidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.Deserialize( + """ + { + "type": "IMGIX" + } + """ + ), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void InvalidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(constant, deserialized); + } +} + +public class AkamaiTest : TestBase +{ + [Fact] + public void DefaultValidation_Works() + { + var constant = new Akamai(); + constant.Validate(); + } + + [Fact] + public void ValidConstantValidation_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.Deserialize( + """ + { + "type": "AKAMAI" + } + """ + ), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + constant.Validate(); + } + + [Fact] + public void InvalidConstantValidationThrows_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + Assert.Throws(() => constant.Validate()); + } + + [Fact] + public void DefaultRoundtrip_Works() + { + var constant = new Akamai(); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void ValidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.Deserialize( + """ + { + "type": "AKAMAI" + } + """ + ), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void InvalidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(constant, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/Accounts/UrlEndpoints/UrlEndpointDeleteParamsTest.cs b/src/Imagekit.Tests/Models/Accounts/UrlEndpoints/UrlEndpointDeleteParamsTest.cs new file mode 100644 index 00000000..576af984 --- /dev/null +++ b/src/Imagekit.Tests/Models/Accounts/UrlEndpoints/UrlEndpointDeleteParamsTest.cs @@ -0,0 +1,39 @@ +using System; +using Imagekit.Models.Accounts.UrlEndpoints; + +namespace Imagekit.Tests.Models.Accounts.UrlEndpoints; + +public class UrlEndpointDeleteParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new UrlEndpointDeleteParams { ID = "id" }; + + string expectedID = "id"; + + Assert.Equal(expectedID, parameters.ID); + } + + [Fact] + public void Url_Works() + { + UrlEndpointDeleteParams parameters = new() { ID = "id" }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/accounts/url-endpoints/id"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new UrlEndpointDeleteParams { ID = "id" }; + + UrlEndpointDeleteParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Accounts/UrlEndpoints/UrlEndpointGetParamsTest.cs b/src/Imagekit.Tests/Models/Accounts/UrlEndpoints/UrlEndpointGetParamsTest.cs new file mode 100644 index 00000000..9a36e40e --- /dev/null +++ b/src/Imagekit.Tests/Models/Accounts/UrlEndpoints/UrlEndpointGetParamsTest.cs @@ -0,0 +1,39 @@ +using System; +using Imagekit.Models.Accounts.UrlEndpoints; + +namespace Imagekit.Tests.Models.Accounts.UrlEndpoints; + +public class UrlEndpointGetParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new UrlEndpointGetParams { ID = "id" }; + + string expectedID = "id"; + + Assert.Equal(expectedID, parameters.ID); + } + + [Fact] + public void Url_Works() + { + UrlEndpointGetParams parameters = new() { ID = "id" }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/accounts/url-endpoints/id"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new UrlEndpointGetParams { ID = "id" }; + + UrlEndpointGetParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Accounts/UrlEndpoints/UrlEndpointListParamsTest.cs b/src/Imagekit.Tests/Models/Accounts/UrlEndpoints/UrlEndpointListParamsTest.cs new file mode 100644 index 00000000..8e3ae6d0 --- /dev/null +++ b/src/Imagekit.Tests/Models/Accounts/UrlEndpoints/UrlEndpointListParamsTest.cs @@ -0,0 +1 @@ +namespace Imagekit.Tests.Models.Accounts.UrlEndpoints; diff --git a/src/Imagekit.Tests/Models/Accounts/UrlEndpoints/UrlEndpointRequestTest.cs b/src/Imagekit.Tests/Models/Accounts/UrlEndpoints/UrlEndpointRequestTest.cs new file mode 100644 index 00000000..c8fe9ffe --- /dev/null +++ b/src/Imagekit.Tests/Models/Accounts/UrlEndpoints/UrlEndpointRequestTest.cs @@ -0,0 +1,570 @@ +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Accounts.UrlEndpoints; + +namespace Imagekit.Tests.Models.Accounts.UrlEndpoints; + +public class UrlEndpointRequestTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new UrlEndpointRequest + { + Description = "My custom URL endpoint", + Origins = ["origin-id-1"], + UrlPrefix = "product-images", + UrlRewriter = new UrlEndpointRequestUrlRewriterCloudinary() + { + PreserveAssetDeliveryTypes = true, + }, + }; + + string expectedDescription = "My custom URL endpoint"; + List expectedOrigins = ["origin-id-1"]; + string expectedUrlPrefix = "product-images"; + UrlEndpointRequestUrlRewriter expectedUrlRewriter = + new UrlEndpointRequestUrlRewriterCloudinary() { PreserveAssetDeliveryTypes = true }; + + Assert.Equal(expectedDescription, model.Description); + Assert.NotNull(model.Origins); + Assert.Equal(expectedOrigins.Count, model.Origins.Count); + for (int i = 0; i < expectedOrigins.Count; i++) + { + Assert.Equal(expectedOrigins[i], model.Origins[i]); + } + Assert.Equal(expectedUrlPrefix, model.UrlPrefix); + Assert.Equal(expectedUrlRewriter, model.UrlRewriter); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new UrlEndpointRequest + { + Description = "My custom URL endpoint", + Origins = ["origin-id-1"], + UrlPrefix = "product-images", + UrlRewriter = new UrlEndpointRequestUrlRewriterCloudinary() + { + PreserveAssetDeliveryTypes = true, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new UrlEndpointRequest + { + Description = "My custom URL endpoint", + Origins = ["origin-id-1"], + UrlPrefix = "product-images", + UrlRewriter = new UrlEndpointRequestUrlRewriterCloudinary() + { + PreserveAssetDeliveryTypes = true, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedDescription = "My custom URL endpoint"; + List expectedOrigins = ["origin-id-1"]; + string expectedUrlPrefix = "product-images"; + UrlEndpointRequestUrlRewriter expectedUrlRewriter = + new UrlEndpointRequestUrlRewriterCloudinary() { PreserveAssetDeliveryTypes = true }; + + Assert.Equal(expectedDescription, deserialized.Description); + Assert.NotNull(deserialized.Origins); + Assert.Equal(expectedOrigins.Count, deserialized.Origins.Count); + for (int i = 0; i < expectedOrigins.Count; i++) + { + Assert.Equal(expectedOrigins[i], deserialized.Origins[i]); + } + Assert.Equal(expectedUrlPrefix, deserialized.UrlPrefix); + Assert.Equal(expectedUrlRewriter, deserialized.UrlRewriter); + } + + [Fact] + public void Validation_Works() + { + var model = new UrlEndpointRequest + { + Description = "My custom URL endpoint", + Origins = ["origin-id-1"], + UrlPrefix = "product-images", + UrlRewriter = new UrlEndpointRequestUrlRewriterCloudinary() + { + PreserveAssetDeliveryTypes = true, + }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new UrlEndpointRequest { Description = "My custom URL endpoint" }; + + Assert.Null(model.Origins); + Assert.False(model.RawData.ContainsKey("origins")); + Assert.Null(model.UrlPrefix); + Assert.False(model.RawData.ContainsKey("urlPrefix")); + Assert.Null(model.UrlRewriter); + Assert.False(model.RawData.ContainsKey("urlRewriter")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new UrlEndpointRequest { Description = "My custom URL endpoint" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new UrlEndpointRequest + { + Description = "My custom URL endpoint", + + // Null should be interpreted as omitted for these properties + Origins = null, + UrlPrefix = null, + UrlRewriter = null, + }; + + Assert.Null(model.Origins); + Assert.False(model.RawData.ContainsKey("origins")); + Assert.Null(model.UrlPrefix); + Assert.False(model.RawData.ContainsKey("urlPrefix")); + Assert.Null(model.UrlRewriter); + Assert.False(model.RawData.ContainsKey("urlRewriter")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new UrlEndpointRequest + { + Description = "My custom URL endpoint", + + // Null should be interpreted as omitted for these properties + Origins = null, + UrlPrefix = null, + UrlRewriter = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new UrlEndpointRequest + { + Description = "My custom URL endpoint", + Origins = ["origin-id-1"], + UrlPrefix = "product-images", + UrlRewriter = new UrlEndpointRequestUrlRewriterCloudinary() + { + PreserveAssetDeliveryTypes = true, + }, + }; + + UrlEndpointRequest copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class UrlEndpointRequestUrlRewriterTest : TestBase +{ + [Fact] + public void CloudinaryValidationWorks() + { + UrlEndpointRequestUrlRewriter value = new UrlEndpointRequestUrlRewriterCloudinary() + { + PreserveAssetDeliveryTypes = true, + }; + value.Validate(); + } + + [Fact] + public void ImgixValidationWorks() + { + UrlEndpointRequestUrlRewriter value = new UrlEndpointRequestUrlRewriterImgix(); + value.Validate(); + } + + [Fact] + public void AkamaiValidationWorks() + { + UrlEndpointRequestUrlRewriter value = new UrlEndpointRequestUrlRewriterAkamai(); + value.Validate(); + } + + [Fact] + public void CloudinarySerializationRoundtripWorks() + { + UrlEndpointRequestUrlRewriter value = new UrlEndpointRequestUrlRewriterCloudinary() + { + PreserveAssetDeliveryTypes = true, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void ImgixSerializationRoundtripWorks() + { + UrlEndpointRequestUrlRewriter value = new UrlEndpointRequestUrlRewriterImgix(); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void AkamaiSerializationRoundtripWorks() + { + UrlEndpointRequestUrlRewriter value = new UrlEndpointRequestUrlRewriterAkamai(); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class UrlEndpointRequestUrlRewriterCloudinaryTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new UrlEndpointRequestUrlRewriterCloudinary + { + PreserveAssetDeliveryTypes = true, + }; + + JsonElement expectedType = JsonSerializer.SerializeToElement("CLOUDINARY"); + bool expectedPreserveAssetDeliveryTypes = true; + + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedPreserveAssetDeliveryTypes, model.PreserveAssetDeliveryTypes); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new UrlEndpointRequestUrlRewriterCloudinary + { + PreserveAssetDeliveryTypes = true, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new UrlEndpointRequestUrlRewriterCloudinary + { + PreserveAssetDeliveryTypes = true, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + JsonElement expectedType = JsonSerializer.SerializeToElement("CLOUDINARY"); + bool expectedPreserveAssetDeliveryTypes = true; + + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedPreserveAssetDeliveryTypes, deserialized.PreserveAssetDeliveryTypes); + } + + [Fact] + public void Validation_Works() + { + var model = new UrlEndpointRequestUrlRewriterCloudinary + { + PreserveAssetDeliveryTypes = true, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new UrlEndpointRequestUrlRewriterCloudinary { }; + + Assert.Null(model.PreserveAssetDeliveryTypes); + Assert.False(model.RawData.ContainsKey("preserveAssetDeliveryTypes")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new UrlEndpointRequestUrlRewriterCloudinary { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new UrlEndpointRequestUrlRewriterCloudinary + { + // Null should be interpreted as omitted for these properties + PreserveAssetDeliveryTypes = null, + }; + + Assert.Null(model.PreserveAssetDeliveryTypes); + Assert.False(model.RawData.ContainsKey("preserveAssetDeliveryTypes")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new UrlEndpointRequestUrlRewriterCloudinary + { + // Null should be interpreted as omitted for these properties + PreserveAssetDeliveryTypes = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new UrlEndpointRequestUrlRewriterCloudinary + { + PreserveAssetDeliveryTypes = true, + }; + + UrlEndpointRequestUrlRewriterCloudinary copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class UrlEndpointRequestUrlRewriterImgixTest : TestBase +{ + [Fact] + public void DefaultValidation_Works() + { + var constant = new UrlEndpointRequestUrlRewriterImgix(); + constant.Validate(); + } + + [Fact] + public void ValidConstantValidation_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.Deserialize( + """ + { + "type": "IMGIX" + } + """ + ), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + constant.Validate(); + } + + [Fact] + public void InvalidConstantValidationThrows_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + Assert.Throws(() => constant.Validate()); + } + + [Fact] + public void DefaultRoundtrip_Works() + { + var constant = new UrlEndpointRequestUrlRewriterImgix(); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void ValidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.Deserialize( + """ + { + "type": "IMGIX" + } + """ + ), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void InvalidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } +} + +public class UrlEndpointRequestUrlRewriterAkamaiTest : TestBase +{ + [Fact] + public void DefaultValidation_Works() + { + var constant = new UrlEndpointRequestUrlRewriterAkamai(); + constant.Validate(); + } + + [Fact] + public void ValidConstantValidation_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.Deserialize( + """ + { + "type": "AKAMAI" + } + """ + ), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + constant.Validate(); + } + + [Fact] + public void InvalidConstantValidationThrows_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + Assert.Throws(() => constant.Validate()); + } + + [Fact] + public void DefaultRoundtrip_Works() + { + var constant = new UrlEndpointRequestUrlRewriterAkamai(); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void ValidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.Deserialize( + """ + { + "type": "AKAMAI" + } + """ + ), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void InvalidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/Accounts/UrlEndpoints/UrlEndpointResponseTest.cs b/src/Imagekit.Tests/Models/Accounts/UrlEndpoints/UrlEndpointResponseTest.cs new file mode 100644 index 00000000..efd7dc2d --- /dev/null +++ b/src/Imagekit.Tests/Models/Accounts/UrlEndpoints/UrlEndpointResponseTest.cs @@ -0,0 +1,520 @@ +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Accounts.UrlEndpoints; + +namespace Imagekit.Tests.Models.Accounts.UrlEndpoints; + +public class UrlEndpointResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new UrlEndpointResponse + { + ID = "id", + Description = "My custom URL endpoint", + Origins = ["origin-id-1", "origin-id-2"], + UrlPrefix = "product-images", + UrlRewriter = new UrlEndpointResponseUrlRewriterCloudinary(true), + }; + + string expectedID = "id"; + string expectedDescription = "My custom URL endpoint"; + List expectedOrigins = ["origin-id-1", "origin-id-2"]; + string expectedUrlPrefix = "product-images"; + UrlEndpointResponseUrlRewriter expectedUrlRewriter = + new UrlEndpointResponseUrlRewriterCloudinary(true); + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedDescription, model.Description); + Assert.Equal(expectedOrigins.Count, model.Origins.Count); + for (int i = 0; i < expectedOrigins.Count; i++) + { + Assert.Equal(expectedOrigins[i], model.Origins[i]); + } + Assert.Equal(expectedUrlPrefix, model.UrlPrefix); + Assert.Equal(expectedUrlRewriter, model.UrlRewriter); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new UrlEndpointResponse + { + ID = "id", + Description = "My custom URL endpoint", + Origins = ["origin-id-1", "origin-id-2"], + UrlPrefix = "product-images", + UrlRewriter = new UrlEndpointResponseUrlRewriterCloudinary(true), + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new UrlEndpointResponse + { + ID = "id", + Description = "My custom URL endpoint", + Origins = ["origin-id-1", "origin-id-2"], + UrlPrefix = "product-images", + UrlRewriter = new UrlEndpointResponseUrlRewriterCloudinary(true), + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedDescription = "My custom URL endpoint"; + List expectedOrigins = ["origin-id-1", "origin-id-2"]; + string expectedUrlPrefix = "product-images"; + UrlEndpointResponseUrlRewriter expectedUrlRewriter = + new UrlEndpointResponseUrlRewriterCloudinary(true); + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedDescription, deserialized.Description); + Assert.Equal(expectedOrigins.Count, deserialized.Origins.Count); + for (int i = 0; i < expectedOrigins.Count; i++) + { + Assert.Equal(expectedOrigins[i], deserialized.Origins[i]); + } + Assert.Equal(expectedUrlPrefix, deserialized.UrlPrefix); + Assert.Equal(expectedUrlRewriter, deserialized.UrlRewriter); + } + + [Fact] + public void Validation_Works() + { + var model = new UrlEndpointResponse + { + ID = "id", + Description = "My custom URL endpoint", + Origins = ["origin-id-1", "origin-id-2"], + UrlPrefix = "product-images", + UrlRewriter = new UrlEndpointResponseUrlRewriterCloudinary(true), + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new UrlEndpointResponse + { + ID = "id", + Description = "My custom URL endpoint", + Origins = ["origin-id-1", "origin-id-2"], + UrlPrefix = "product-images", + }; + + Assert.Null(model.UrlRewriter); + Assert.False(model.RawData.ContainsKey("urlRewriter")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new UrlEndpointResponse + { + ID = "id", + Description = "My custom URL endpoint", + Origins = ["origin-id-1", "origin-id-2"], + UrlPrefix = "product-images", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new UrlEndpointResponse + { + ID = "id", + Description = "My custom URL endpoint", + Origins = ["origin-id-1", "origin-id-2"], + UrlPrefix = "product-images", + + // Null should be interpreted as omitted for these properties + UrlRewriter = null, + }; + + Assert.Null(model.UrlRewriter); + Assert.False(model.RawData.ContainsKey("urlRewriter")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new UrlEndpointResponse + { + ID = "id", + Description = "My custom URL endpoint", + Origins = ["origin-id-1", "origin-id-2"], + UrlPrefix = "product-images", + + // Null should be interpreted as omitted for these properties + UrlRewriter = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new UrlEndpointResponse + { + ID = "id", + Description = "My custom URL endpoint", + Origins = ["origin-id-1", "origin-id-2"], + UrlPrefix = "product-images", + UrlRewriter = new UrlEndpointResponseUrlRewriterCloudinary(true), + }; + + UrlEndpointResponse copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class UrlEndpointResponseUrlRewriterTest : TestBase +{ + [Fact] + public void CloudinaryValidationWorks() + { + UrlEndpointResponseUrlRewriter value = new UrlEndpointResponseUrlRewriterCloudinary(true); + value.Validate(); + } + + [Fact] + public void ImgixValidationWorks() + { + UrlEndpointResponseUrlRewriter value = new UrlEndpointResponseUrlRewriterImgix(); + value.Validate(); + } + + [Fact] + public void AkamaiValidationWorks() + { + UrlEndpointResponseUrlRewriter value = new UrlEndpointResponseUrlRewriterAkamai(); + value.Validate(); + } + + [Fact] + public void CloudinarySerializationRoundtripWorks() + { + UrlEndpointResponseUrlRewriter value = new UrlEndpointResponseUrlRewriterCloudinary(true); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void ImgixSerializationRoundtripWorks() + { + UrlEndpointResponseUrlRewriter value = new UrlEndpointResponseUrlRewriterImgix(); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void AkamaiSerializationRoundtripWorks() + { + UrlEndpointResponseUrlRewriter value = new UrlEndpointResponseUrlRewriterAkamai(); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class UrlEndpointResponseUrlRewriterCloudinaryTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new UrlEndpointResponseUrlRewriterCloudinary + { + PreserveAssetDeliveryTypes = true, + }; + + bool expectedPreserveAssetDeliveryTypes = true; + JsonElement expectedType = JsonSerializer.SerializeToElement("CLOUDINARY"); + + Assert.Equal(expectedPreserveAssetDeliveryTypes, model.PreserveAssetDeliveryTypes); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new UrlEndpointResponseUrlRewriterCloudinary + { + PreserveAssetDeliveryTypes = true, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new UrlEndpointResponseUrlRewriterCloudinary + { + PreserveAssetDeliveryTypes = true, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + bool expectedPreserveAssetDeliveryTypes = true; + JsonElement expectedType = JsonSerializer.SerializeToElement("CLOUDINARY"); + + Assert.Equal(expectedPreserveAssetDeliveryTypes, deserialized.PreserveAssetDeliveryTypes); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + } + + [Fact] + public void Validation_Works() + { + var model = new UrlEndpointResponseUrlRewriterCloudinary + { + PreserveAssetDeliveryTypes = true, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new UrlEndpointResponseUrlRewriterCloudinary + { + PreserveAssetDeliveryTypes = true, + }; + + UrlEndpointResponseUrlRewriterCloudinary copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class UrlEndpointResponseUrlRewriterImgixTest : TestBase +{ + [Fact] + public void DefaultValidation_Works() + { + var constant = new UrlEndpointResponseUrlRewriterImgix(); + constant.Validate(); + } + + [Fact] + public void ValidConstantValidation_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.Deserialize( + """ + { + "type": "IMGIX" + } + """ + ), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + constant.Validate(); + } + + [Fact] + public void InvalidConstantValidationThrows_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + Assert.Throws(() => constant.Validate()); + } + + [Fact] + public void DefaultRoundtrip_Works() + { + var constant = new UrlEndpointResponseUrlRewriterImgix(); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void ValidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.Deserialize( + """ + { + "type": "IMGIX" + } + """ + ), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void InvalidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } +} + +public class UrlEndpointResponseUrlRewriterAkamaiTest : TestBase +{ + [Fact] + public void DefaultValidation_Works() + { + var constant = new UrlEndpointResponseUrlRewriterAkamai(); + constant.Validate(); + } + + [Fact] + public void ValidConstantValidation_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.Deserialize( + """ + { + "type": "AKAMAI" + } + """ + ), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + constant.Validate(); + } + + [Fact] + public void InvalidConstantValidationThrows_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + Assert.Throws(() => constant.Validate()); + } + + [Fact] + public void DefaultRoundtrip_Works() + { + var constant = new UrlEndpointResponseUrlRewriterAkamai(); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void ValidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.Deserialize( + """ + { + "type": "AKAMAI" + } + """ + ), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void InvalidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/Accounts/UrlEndpoints/UrlEndpointUpdateParamsTest.cs b/src/Imagekit.Tests/Models/Accounts/UrlEndpoints/UrlEndpointUpdateParamsTest.cs new file mode 100644 index 00000000..0ac9405b --- /dev/null +++ b/src/Imagekit.Tests/Models/Accounts/UrlEndpoints/UrlEndpointUpdateParamsTest.cs @@ -0,0 +1,499 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Accounts.UrlEndpoints; + +namespace Imagekit.Tests.Models.Accounts.UrlEndpoints; + +public class UrlEndpointUpdateParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new UrlEndpointUpdateParams + { + ID = "id", + Description = "My custom URL endpoint", + Origins = ["origin-id-1"], + UrlPrefix = "product-images", + UrlRewriter = new UrlEndpointUpdateParamsUrlRewriterCloudinary() + { + PreserveAssetDeliveryTypes = true, + }, + }; + + string expectedID = "id"; + string expectedDescription = "My custom URL endpoint"; + List expectedOrigins = ["origin-id-1"]; + string expectedUrlPrefix = "product-images"; + UrlEndpointUpdateParamsUrlRewriter expectedUrlRewriter = + new UrlEndpointUpdateParamsUrlRewriterCloudinary() + { + PreserveAssetDeliveryTypes = true, + }; + + Assert.Equal(expectedID, parameters.ID); + Assert.Equal(expectedDescription, parameters.Description); + Assert.NotNull(parameters.Origins); + Assert.Equal(expectedOrigins.Count, parameters.Origins.Count); + for (int i = 0; i < expectedOrigins.Count; i++) + { + Assert.Equal(expectedOrigins[i], parameters.Origins[i]); + } + Assert.Equal(expectedUrlPrefix, parameters.UrlPrefix); + Assert.Equal(expectedUrlRewriter, parameters.UrlRewriter); + } + + [Fact] + public void OptionalNonNullableParamsUnsetAreNotSet_Works() + { + var parameters = new UrlEndpointUpdateParams + { + ID = "id", + Description = "My custom URL endpoint", + }; + + Assert.Null(parameters.Origins); + Assert.False(parameters.RawBodyData.ContainsKey("origins")); + Assert.Null(parameters.UrlPrefix); + Assert.False(parameters.RawBodyData.ContainsKey("urlPrefix")); + Assert.Null(parameters.UrlRewriter); + Assert.False(parameters.RawBodyData.ContainsKey("urlRewriter")); + } + + [Fact] + public void OptionalNonNullableParamsSetToNullAreNotSet_Works() + { + var parameters = new UrlEndpointUpdateParams + { + ID = "id", + Description = "My custom URL endpoint", + + // Null should be interpreted as omitted for these properties + Origins = null, + UrlPrefix = null, + UrlRewriter = null, + }; + + Assert.Null(parameters.Origins); + Assert.False(parameters.RawBodyData.ContainsKey("origins")); + Assert.Null(parameters.UrlPrefix); + Assert.False(parameters.RawBodyData.ContainsKey("urlPrefix")); + Assert.Null(parameters.UrlRewriter); + Assert.False(parameters.RawBodyData.ContainsKey("urlRewriter")); + } + + [Fact] + public void Url_Works() + { + UrlEndpointUpdateParams parameters = new() + { + ID = "id", + Description = "My custom URL endpoint", + }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/accounts/url-endpoints/id"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new UrlEndpointUpdateParams + { + ID = "id", + Description = "My custom URL endpoint", + Origins = ["origin-id-1"], + UrlPrefix = "product-images", + UrlRewriter = new UrlEndpointUpdateParamsUrlRewriterCloudinary() + { + PreserveAssetDeliveryTypes = true, + }, + }; + + UrlEndpointUpdateParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} + +public class UrlEndpointUpdateParamsUrlRewriterTest : TestBase +{ + [Fact] + public void CloudinaryValidationWorks() + { + UrlEndpointUpdateParamsUrlRewriter value = + new UrlEndpointUpdateParamsUrlRewriterCloudinary() + { + PreserveAssetDeliveryTypes = true, + }; + value.Validate(); + } + + [Fact] + public void ImgixValidationWorks() + { + UrlEndpointUpdateParamsUrlRewriter value = new UrlEndpointUpdateParamsUrlRewriterImgix(); + value.Validate(); + } + + [Fact] + public void AkamaiValidationWorks() + { + UrlEndpointUpdateParamsUrlRewriter value = new UrlEndpointUpdateParamsUrlRewriterAkamai(); + value.Validate(); + } + + [Fact] + public void CloudinarySerializationRoundtripWorks() + { + UrlEndpointUpdateParamsUrlRewriter value = + new UrlEndpointUpdateParamsUrlRewriterCloudinary() + { + PreserveAssetDeliveryTypes = true, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void ImgixSerializationRoundtripWorks() + { + UrlEndpointUpdateParamsUrlRewriter value = new UrlEndpointUpdateParamsUrlRewriterImgix(); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void AkamaiSerializationRoundtripWorks() + { + UrlEndpointUpdateParamsUrlRewriter value = new UrlEndpointUpdateParamsUrlRewriterAkamai(); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class UrlEndpointUpdateParamsUrlRewriterCloudinaryTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new UrlEndpointUpdateParamsUrlRewriterCloudinary + { + PreserveAssetDeliveryTypes = true, + }; + + JsonElement expectedType = JsonSerializer.SerializeToElement("CLOUDINARY"); + bool expectedPreserveAssetDeliveryTypes = true; + + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedPreserveAssetDeliveryTypes, model.PreserveAssetDeliveryTypes); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new UrlEndpointUpdateParamsUrlRewriterCloudinary + { + PreserveAssetDeliveryTypes = true, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new UrlEndpointUpdateParamsUrlRewriterCloudinary + { + PreserveAssetDeliveryTypes = true, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + JsonElement expectedType = JsonSerializer.SerializeToElement("CLOUDINARY"); + bool expectedPreserveAssetDeliveryTypes = true; + + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedPreserveAssetDeliveryTypes, deserialized.PreserveAssetDeliveryTypes); + } + + [Fact] + public void Validation_Works() + { + var model = new UrlEndpointUpdateParamsUrlRewriterCloudinary + { + PreserveAssetDeliveryTypes = true, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new UrlEndpointUpdateParamsUrlRewriterCloudinary { }; + + Assert.Null(model.PreserveAssetDeliveryTypes); + Assert.False(model.RawData.ContainsKey("preserveAssetDeliveryTypes")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new UrlEndpointUpdateParamsUrlRewriterCloudinary { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new UrlEndpointUpdateParamsUrlRewriterCloudinary + { + // Null should be interpreted as omitted for these properties + PreserveAssetDeliveryTypes = null, + }; + + Assert.Null(model.PreserveAssetDeliveryTypes); + Assert.False(model.RawData.ContainsKey("preserveAssetDeliveryTypes")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new UrlEndpointUpdateParamsUrlRewriterCloudinary + { + // Null should be interpreted as omitted for these properties + PreserveAssetDeliveryTypes = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new UrlEndpointUpdateParamsUrlRewriterCloudinary + { + PreserveAssetDeliveryTypes = true, + }; + + UrlEndpointUpdateParamsUrlRewriterCloudinary copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class UrlEndpointUpdateParamsUrlRewriterImgixTest : TestBase +{ + [Fact] + public void DefaultValidation_Works() + { + var constant = new UrlEndpointUpdateParamsUrlRewriterImgix(); + constant.Validate(); + } + + [Fact] + public void ValidConstantValidation_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.Deserialize( + """ + { + "type": "IMGIX" + } + """ + ), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + constant.Validate(); + } + + [Fact] + public void InvalidConstantValidationThrows_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + Assert.Throws(() => constant.Validate()); + } + + [Fact] + public void DefaultRoundtrip_Works() + { + var constant = new UrlEndpointUpdateParamsUrlRewriterImgix(); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void ValidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.Deserialize( + """ + { + "type": "IMGIX" + } + """ + ), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void InvalidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } +} + +public class UrlEndpointUpdateParamsUrlRewriterAkamaiTest : TestBase +{ + [Fact] + public void DefaultValidation_Works() + { + var constant = new UrlEndpointUpdateParamsUrlRewriterAkamai(); + constant.Validate(); + } + + [Fact] + public void ValidConstantValidation_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.Deserialize( + """ + { + "type": "AKAMAI" + } + """ + ), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + constant.Validate(); + } + + [Fact] + public void InvalidConstantValidationThrows_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + Assert.Throws(() => constant.Validate()); + } + + [Fact] + public void DefaultRoundtrip_Works() + { + var constant = new UrlEndpointUpdateParamsUrlRewriterAkamai(); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void ValidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.Deserialize( + """ + { + "type": "AKAMAI" + } + """ + ), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void InvalidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/Accounts/Usage/UsageGetParamsTest.cs b/src/Imagekit.Tests/Models/Accounts/Usage/UsageGetParamsTest.cs new file mode 100644 index 00000000..d27db510 --- /dev/null +++ b/src/Imagekit.Tests/Models/Accounts/Usage/UsageGetParamsTest.cs @@ -0,0 +1,46 @@ +using System; +using Imagekit.Models.Accounts.Usage; + +namespace Imagekit.Tests.Models.Accounts.Usage; + +public class UsageGetParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new UsageGetParams { EndDate = "2019-12-27", StartDate = "2019-12-27" }; + + string expectedEndDate = "2019-12-27"; + string expectedStartDate = "2019-12-27"; + + Assert.Equal(expectedEndDate, parameters.EndDate); + Assert.Equal(expectedStartDate, parameters.StartDate); + } + + [Fact] + public void Url_Works() + { + UsageGetParams parameters = new() { EndDate = "2019-12-27", StartDate = "2019-12-27" }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual( + new Uri( + "https://api.imagekit.io/v1/accounts/usage?endDate=2019-12-27&startDate=2019-12-27" + ), + url + ) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new UsageGetParams { EndDate = "2019-12-27", StartDate = "2019-12-27" }; + + UsageGetParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Accounts/Usage/UsageGetResponseTest.cs b/src/Imagekit.Tests/Models/Accounts/Usage/UsageGetResponseTest.cs new file mode 100644 index 00000000..584f1962 --- /dev/null +++ b/src/Imagekit.Tests/Models/Accounts/Usage/UsageGetResponseTest.cs @@ -0,0 +1,184 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Accounts.Usage; + +namespace Imagekit.Tests.Models.Accounts.Usage; + +public class UsageGetResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new UsageGetResponse + { + BandwidthBytes = 0, + ExtensionUnitsCount = 0, + MediaLibraryStorageBytes = 0, + OriginalCacheStorageBytes = 0, + VideoProcessingUnitsCount = 0, + }; + + long expectedBandwidthBytes = 0; + long expectedExtensionUnitsCount = 0; + long expectedMediaLibraryStorageBytes = 0; + long expectedOriginalCacheStorageBytes = 0; + long expectedVideoProcessingUnitsCount = 0; + + Assert.Equal(expectedBandwidthBytes, model.BandwidthBytes); + Assert.Equal(expectedExtensionUnitsCount, model.ExtensionUnitsCount); + Assert.Equal(expectedMediaLibraryStorageBytes, model.MediaLibraryStorageBytes); + Assert.Equal(expectedOriginalCacheStorageBytes, model.OriginalCacheStorageBytes); + Assert.Equal(expectedVideoProcessingUnitsCount, model.VideoProcessingUnitsCount); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new UsageGetResponse + { + BandwidthBytes = 0, + ExtensionUnitsCount = 0, + MediaLibraryStorageBytes = 0, + OriginalCacheStorageBytes = 0, + VideoProcessingUnitsCount = 0, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new UsageGetResponse + { + BandwidthBytes = 0, + ExtensionUnitsCount = 0, + MediaLibraryStorageBytes = 0, + OriginalCacheStorageBytes = 0, + VideoProcessingUnitsCount = 0, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + long expectedBandwidthBytes = 0; + long expectedExtensionUnitsCount = 0; + long expectedMediaLibraryStorageBytes = 0; + long expectedOriginalCacheStorageBytes = 0; + long expectedVideoProcessingUnitsCount = 0; + + Assert.Equal(expectedBandwidthBytes, deserialized.BandwidthBytes); + Assert.Equal(expectedExtensionUnitsCount, deserialized.ExtensionUnitsCount); + Assert.Equal(expectedMediaLibraryStorageBytes, deserialized.MediaLibraryStorageBytes); + Assert.Equal(expectedOriginalCacheStorageBytes, deserialized.OriginalCacheStorageBytes); + Assert.Equal(expectedVideoProcessingUnitsCount, deserialized.VideoProcessingUnitsCount); + } + + [Fact] + public void Validation_Works() + { + var model = new UsageGetResponse + { + BandwidthBytes = 0, + ExtensionUnitsCount = 0, + MediaLibraryStorageBytes = 0, + OriginalCacheStorageBytes = 0, + VideoProcessingUnitsCount = 0, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new UsageGetResponse { }; + + Assert.Null(model.BandwidthBytes); + Assert.False(model.RawData.ContainsKey("bandwidthBytes")); + Assert.Null(model.ExtensionUnitsCount); + Assert.False(model.RawData.ContainsKey("extensionUnitsCount")); + Assert.Null(model.MediaLibraryStorageBytes); + Assert.False(model.RawData.ContainsKey("mediaLibraryStorageBytes")); + Assert.Null(model.OriginalCacheStorageBytes); + Assert.False(model.RawData.ContainsKey("originalCacheStorageBytes")); + Assert.Null(model.VideoProcessingUnitsCount); + Assert.False(model.RawData.ContainsKey("videoProcessingUnitsCount")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new UsageGetResponse { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new UsageGetResponse + { + // Null should be interpreted as omitted for these properties + BandwidthBytes = null, + ExtensionUnitsCount = null, + MediaLibraryStorageBytes = null, + OriginalCacheStorageBytes = null, + VideoProcessingUnitsCount = null, + }; + + Assert.Null(model.BandwidthBytes); + Assert.False(model.RawData.ContainsKey("bandwidthBytes")); + Assert.Null(model.ExtensionUnitsCount); + Assert.False(model.RawData.ContainsKey("extensionUnitsCount")); + Assert.Null(model.MediaLibraryStorageBytes); + Assert.False(model.RawData.ContainsKey("mediaLibraryStorageBytes")); + Assert.Null(model.OriginalCacheStorageBytes); + Assert.False(model.RawData.ContainsKey("originalCacheStorageBytes")); + Assert.Null(model.VideoProcessingUnitsCount); + Assert.False(model.RawData.ContainsKey("videoProcessingUnitsCount")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new UsageGetResponse + { + // Null should be interpreted as omitted for these properties + BandwidthBytes = null, + ExtensionUnitsCount = null, + MediaLibraryStorageBytes = null, + OriginalCacheStorageBytes = null, + VideoProcessingUnitsCount = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new UsageGetResponse + { + BandwidthBytes = 0, + ExtensionUnitsCount = 0, + MediaLibraryStorageBytes = 0, + OriginalCacheStorageBytes = 0, + VideoProcessingUnitsCount = 0, + }; + + UsageGetResponse copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Assets/AssetListParamsTest.cs b/src/Imagekit.Tests/Models/Assets/AssetListParamsTest.cs new file mode 100644 index 00000000..e9c5c555 --- /dev/null +++ b/src/Imagekit.Tests/Models/Assets/AssetListParamsTest.cs @@ -0,0 +1,342 @@ +using System; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Assets = Imagekit.Models.Assets; + +namespace Imagekit.Tests.Models.Assets; + +public class AssetListParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new Assets::AssetListParams + { + FileType = Assets::FileType.All, + Limit = 1, + Path = "path", + SearchQuery = "searchQuery", + Skip = 0, + Sort = Assets::Sort.AscName, + Type = Assets::Type.File, + }; + + ApiEnum expectedFileType = Assets::FileType.All; + long expectedLimit = 1; + string expectedPath = "path"; + string expectedSearchQuery = "searchQuery"; + long expectedSkip = 0; + ApiEnum expectedSort = Assets::Sort.AscName; + ApiEnum expectedType = Assets::Type.File; + + Assert.Equal(expectedFileType, parameters.FileType); + Assert.Equal(expectedLimit, parameters.Limit); + Assert.Equal(expectedPath, parameters.Path); + Assert.Equal(expectedSearchQuery, parameters.SearchQuery); + Assert.Equal(expectedSkip, parameters.Skip); + Assert.Equal(expectedSort, parameters.Sort); + Assert.Equal(expectedType, parameters.Type); + } + + [Fact] + public void OptionalNonNullableParamsUnsetAreNotSet_Works() + { + var parameters = new Assets::AssetListParams { }; + + Assert.Null(parameters.FileType); + Assert.False(parameters.RawQueryData.ContainsKey("fileType")); + Assert.Null(parameters.Limit); + Assert.False(parameters.RawQueryData.ContainsKey("limit")); + Assert.Null(parameters.Path); + Assert.False(parameters.RawQueryData.ContainsKey("path")); + Assert.Null(parameters.SearchQuery); + Assert.False(parameters.RawQueryData.ContainsKey("searchQuery")); + Assert.Null(parameters.Skip); + Assert.False(parameters.RawQueryData.ContainsKey("skip")); + Assert.Null(parameters.Sort); + Assert.False(parameters.RawQueryData.ContainsKey("sort")); + Assert.Null(parameters.Type); + Assert.False(parameters.RawQueryData.ContainsKey("type")); + } + + [Fact] + public void OptionalNonNullableParamsSetToNullAreNotSet_Works() + { + var parameters = new Assets::AssetListParams + { + // Null should be interpreted as omitted for these properties + FileType = null, + Limit = null, + Path = null, + SearchQuery = null, + Skip = null, + Sort = null, + Type = null, + }; + + Assert.Null(parameters.FileType); + Assert.False(parameters.RawQueryData.ContainsKey("fileType")); + Assert.Null(parameters.Limit); + Assert.False(parameters.RawQueryData.ContainsKey("limit")); + Assert.Null(parameters.Path); + Assert.False(parameters.RawQueryData.ContainsKey("path")); + Assert.Null(parameters.SearchQuery); + Assert.False(parameters.RawQueryData.ContainsKey("searchQuery")); + Assert.Null(parameters.Skip); + Assert.False(parameters.RawQueryData.ContainsKey("skip")); + Assert.Null(parameters.Sort); + Assert.False(parameters.RawQueryData.ContainsKey("sort")); + Assert.Null(parameters.Type); + Assert.False(parameters.RawQueryData.ContainsKey("type")); + } + + [Fact] + public void Url_Works() + { + Assets::AssetListParams parameters = new() + { + FileType = Assets::FileType.All, + Limit = 1, + Path = "path", + SearchQuery = "searchQuery", + Skip = 0, + Sort = Assets::Sort.AscName, + Type = Assets::Type.File, + }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual( + new Uri( + "https://api.imagekit.io/v1/files?fileType=all&limit=1&path=path&searchQuery=searchQuery&skip=0&sort=ASC_NAME&type=file" + ), + url + ) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new Assets::AssetListParams + { + FileType = Assets::FileType.All, + Limit = 1, + Path = "path", + SearchQuery = "searchQuery", + Skip = 0, + Sort = Assets::Sort.AscName, + Type = Assets::Type.File, + }; + + Assets::AssetListParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} + +public class FileTypeTest : TestBase +{ + [Theory] + [InlineData(Assets::FileType.All)] + [InlineData(Assets::FileType.Image)] + [InlineData(Assets::FileType.NonImage)] + public void Validation_Works(Assets::FileType rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Assets::FileType.All)] + [InlineData(Assets::FileType.Image)] + [InlineData(Assets::FileType.NonImage)] + public void SerializationRoundtrip_Works(Assets::FileType rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class SortTest : TestBase +{ + [Theory] + [InlineData(Assets::Sort.AscName)] + [InlineData(Assets::Sort.DescName)] + [InlineData(Assets::Sort.AscCreated)] + [InlineData(Assets::Sort.DescCreated)] + [InlineData(Assets::Sort.AscUpdated)] + [InlineData(Assets::Sort.DescUpdated)] + [InlineData(Assets::Sort.AscHeight)] + [InlineData(Assets::Sort.DescHeight)] + [InlineData(Assets::Sort.AscWidth)] + [InlineData(Assets::Sort.DescWidth)] + [InlineData(Assets::Sort.AscSize)] + [InlineData(Assets::Sort.DescSize)] + [InlineData(Assets::Sort.AscRelevance)] + [InlineData(Assets::Sort.DescRelevance)] + public void Validation_Works(Assets::Sort rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Assets::Sort.AscName)] + [InlineData(Assets::Sort.DescName)] + [InlineData(Assets::Sort.AscCreated)] + [InlineData(Assets::Sort.DescCreated)] + [InlineData(Assets::Sort.AscUpdated)] + [InlineData(Assets::Sort.DescUpdated)] + [InlineData(Assets::Sort.AscHeight)] + [InlineData(Assets::Sort.DescHeight)] + [InlineData(Assets::Sort.AscWidth)] + [InlineData(Assets::Sort.DescWidth)] + [InlineData(Assets::Sort.AscSize)] + [InlineData(Assets::Sort.DescSize)] + [InlineData(Assets::Sort.AscRelevance)] + [InlineData(Assets::Sort.DescRelevance)] + public void SerializationRoundtrip_Works(Assets::Sort rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class TypeTest : TestBase +{ + [Theory] + [InlineData(Assets::Type.File)] + [InlineData(Assets::Type.FileVersion)] + [InlineData(Assets::Type.Folder)] + [InlineData(Assets::Type.All)] + public void Validation_Works(Assets::Type rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Assets::Type.File)] + [InlineData(Assets::Type.FileVersion)] + [InlineData(Assets::Type.Folder)] + [InlineData(Assets::Type.All)] + public void SerializationRoundtrip_Works(Assets::Type rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/Assets/AssetListResponseTest.cs b/src/Imagekit.Tests/Models/Assets/AssetListResponseTest.cs new file mode 100644 index 00000000..50942326 --- /dev/null +++ b/src/Imagekit.Tests/Models/Assets/AssetListResponseTest.cs @@ -0,0 +1,212 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Assets; +using Files = Imagekit.Models.Files; +using Models = Imagekit.Models; + +namespace Imagekit.Tests.Models.Assets; + +public class AssetListResponseTest : TestBase +{ + [Fact] + public void FileValidationWorks() + { + AssetListResponse value = new Files::File() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + value.Validate(); + } + + [Fact] + public void FolderValidationWorks() + { + AssetListResponse value = new Files::Folder() + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FolderID = "folderId", + FolderPath = "folderPath", + Name = "name", + Type = Files::FolderType.Folder, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + }; + value.Validate(); + } + + [Fact] + public void FileSerializationRoundtripWorks() + { + AssetListResponse value = new Files::File() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void FolderSerializationRoundtripWorks() + { + AssetListResponse value = new Files::Folder() + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FolderID = "folderId", + FolderPath = "folderPath", + Name = "name", + Type = Files::FolderType.Folder, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/BaseOverlayTest.cs b/src/Imagekit.Tests/Models/BaseOverlayTest.cs new file mode 100644 index 00000000..6f0383dc --- /dev/null +++ b/src/Imagekit.Tests/Models/BaseOverlayTest.cs @@ -0,0 +1,308 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class BaseOverlayTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new BaseOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + }; + + ApiEnum expectedLayerMode = LayerMode.Multiply; + OverlayPosition expectedPosition = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }; + OverlayTiming expectedTiming = new() + { + Duration = 0, + End = 0, + Start = 0, + }; + + Assert.Equal(expectedLayerMode, model.LayerMode); + Assert.Equal(expectedPosition, model.Position); + Assert.Equal(expectedTiming, model.Timing); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new BaseOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new BaseOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum expectedLayerMode = LayerMode.Multiply; + OverlayPosition expectedPosition = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }; + OverlayTiming expectedTiming = new() + { + Duration = 0, + End = 0, + Start = 0, + }; + + Assert.Equal(expectedLayerMode, deserialized.LayerMode); + Assert.Equal(expectedPosition, deserialized.Position); + Assert.Equal(expectedTiming, deserialized.Timing); + } + + [Fact] + public void Validation_Works() + { + var model = new BaseOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new BaseOverlay { }; + + Assert.Null(model.LayerMode); + Assert.False(model.RawData.ContainsKey("layerMode")); + Assert.Null(model.Position); + Assert.False(model.RawData.ContainsKey("position")); + Assert.Null(model.Timing); + Assert.False(model.RawData.ContainsKey("timing")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new BaseOverlay { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new BaseOverlay + { + // Null should be interpreted as omitted for these properties + LayerMode = null, + Position = null, + Timing = null, + }; + + Assert.Null(model.LayerMode); + Assert.False(model.RawData.ContainsKey("layerMode")); + Assert.Null(model.Position); + Assert.False(model.RawData.ContainsKey("position")); + Assert.Null(model.Timing); + Assert.False(model.RawData.ContainsKey("timing")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new BaseOverlay + { + // Null should be interpreted as omitted for these properties + LayerMode = null, + Position = null, + Timing = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new BaseOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + }; + + BaseOverlay copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class LayerModeTest : TestBase +{ + [Theory] + [InlineData(LayerMode.Multiply)] + [InlineData(LayerMode.Cutter)] + [InlineData(LayerMode.Cutout)] + [InlineData(LayerMode.Displace)] + public void Validation_Works(LayerMode rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(LayerMode.Multiply)] + [InlineData(LayerMode.Cutter)] + [InlineData(LayerMode.Cutout)] + [InlineData(LayerMode.Displace)] + public void SerializationRoundtrip_Works(LayerMode rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/Beta/V2/Files/FileUploadParamsTest.cs b/src/Imagekit.Tests/Models/Beta/V2/Files/FileUploadParamsTest.cs new file mode 100644 index 00000000..6da89b2b --- /dev/null +++ b/src/Imagekit.Tests/Models/Beta/V2/Files/FileUploadParamsTest.cs @@ -0,0 +1,1202 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Beta.V2.Files; +using Models = Imagekit.Models; + +namespace Imagekit.Tests.Models.Beta.V2.Files; + +public class FileUploadParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + BinaryContent file = Encoding.UTF8.GetBytes("Example data"); + + var parameters = new FileUploadParams + { + File = file, + FileName = "fileName", + Token = "token", + Checks = "\"request.folder\" : \"marketing/\"\n", + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "brand", JsonSerializer.SerializeToElement("bar") }, + { "color", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "Running shoes", + Extensions = + [ + new Models::ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new Models::ExtensionItemAutoTaggingExtension() + { + MaxTags = 5, + MinConfidence = 95, + Name = Models::ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new Models::ExtensionItemAIAutoDescription(), + new Models::ExtensionItemAITasks( + [ + new Models::ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + new Models::ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }, + ] + ), + new Models::SavedExtension("ext_abc123"), + ], + Folder = "folder", + IsPrivateFile = true, + IsPublished = true, + OverwriteAITags = true, + OverwriteCustomMetadata = true, + OverwriteFile = true, + OverwriteTags = true, + ResponseFields = + [ + ResponseField.Tags, + ResponseField.CustomCoordinates, + ResponseField.IsPrivateFile, + ], + Tags = ["t-shirt", "round-neck", "men"], + Transformation = new() + { + Post = + [ + new Thumbnail() { Value = "w-150,h-150" }, + new Abs() { Protocol = Protocol.Dash, Value = "sr-240_360_480_720_1080" }, + ], + Pre = "w-300,h-300,q-80", + }, + UseUniqueFileName = true, + WebhookUrl = "https://example.com", + }; + + BinaryContent expectedFile = file; + string expectedFileName = "fileName"; + string expectedToken = "token"; + string expectedChecks = "\"request.folder\" : \"marketing/\"\n"; + string expectedCustomCoordinates = "customCoordinates"; + Dictionary expectedCustomMetadata = new() + { + { "brand", JsonSerializer.SerializeToElement("bar") }, + { "color", JsonSerializer.SerializeToElement("bar") }, + }; + string expectedDescription = "Running shoes"; + List expectedExtensions = + [ + new Models::ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new Models::ExtensionItemAutoTaggingExtension() + { + MaxTags = 5, + MinConfidence = 95, + Name = Models::ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new Models::ExtensionItemAIAutoDescription(), + new Models::ExtensionItemAITasks( + [ + new Models::ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + new Models::ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }, + ] + ), + new Models::SavedExtension("ext_abc123"), + ]; + string expectedFolder = "folder"; + bool expectedIsPrivateFile = true; + bool expectedIsPublished = true; + bool expectedOverwriteAITags = true; + bool expectedOverwriteCustomMetadata = true; + bool expectedOverwriteFile = true; + bool expectedOverwriteTags = true; + List> expectedResponseFields = + [ + ResponseField.Tags, + ResponseField.CustomCoordinates, + ResponseField.IsPrivateFile, + ]; + List expectedTags = ["t-shirt", "round-neck", "men"]; + Transformation expectedTransformation = new() + { + Post = + [ + new Thumbnail() { Value = "w-150,h-150" }, + new Abs() { Protocol = Protocol.Dash, Value = "sr-240_360_480_720_1080" }, + ], + Pre = "w-300,h-300,q-80", + }; + bool expectedUseUniqueFileName = true; + string expectedWebhookUrl = "https://example.com"; + + Assert.Equal(expectedFile, parameters.File); + Assert.Equal(expectedFileName, parameters.FileName); + Assert.Equal(expectedToken, parameters.Token); + Assert.Equal(expectedChecks, parameters.Checks); + Assert.Equal(expectedCustomCoordinates, parameters.CustomCoordinates); + Assert.NotNull(parameters.CustomMetadata); + Assert.Equal(expectedCustomMetadata.Count, parameters.CustomMetadata.Count); + foreach (var item in expectedCustomMetadata) + { + Assert.True(parameters.CustomMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, parameters.CustomMetadata[item.Key])); + } + Assert.Equal(expectedDescription, parameters.Description); + Assert.NotNull(parameters.Extensions); + Assert.Equal(expectedExtensions.Count, parameters.Extensions.Count); + for (int i = 0; i < expectedExtensions.Count; i++) + { + Assert.Equal(expectedExtensions[i], parameters.Extensions[i]); + } + Assert.Equal(expectedFolder, parameters.Folder); + Assert.Equal(expectedIsPrivateFile, parameters.IsPrivateFile); + Assert.Equal(expectedIsPublished, parameters.IsPublished); + Assert.Equal(expectedOverwriteAITags, parameters.OverwriteAITags); + Assert.Equal(expectedOverwriteCustomMetadata, parameters.OverwriteCustomMetadata); + Assert.Equal(expectedOverwriteFile, parameters.OverwriteFile); + Assert.Equal(expectedOverwriteTags, parameters.OverwriteTags); + Assert.NotNull(parameters.ResponseFields); + Assert.Equal(expectedResponseFields.Count, parameters.ResponseFields.Count); + for (int i = 0; i < expectedResponseFields.Count; i++) + { + Assert.Equal(expectedResponseFields[i], parameters.ResponseFields[i]); + } + Assert.NotNull(parameters.Tags); + Assert.Equal(expectedTags.Count, parameters.Tags.Count); + for (int i = 0; i < expectedTags.Count; i++) + { + Assert.Equal(expectedTags[i], parameters.Tags[i]); + } + Assert.Equal(expectedTransformation, parameters.Transformation); + Assert.Equal(expectedUseUniqueFileName, parameters.UseUniqueFileName); + Assert.Equal(expectedWebhookUrl, parameters.WebhookUrl); + } + + [Fact] + public void OptionalNonNullableParamsUnsetAreNotSet_Works() + { + BinaryContent file = Encoding.UTF8.GetBytes("Example data"); + + var parameters = new FileUploadParams { File = file, FileName = "fileName" }; + + Assert.Null(parameters.Token); + Assert.False(parameters.RawBodyData.ContainsKey("token")); + Assert.Null(parameters.Checks); + Assert.False(parameters.RawBodyData.ContainsKey("checks")); + Assert.Null(parameters.CustomCoordinates); + Assert.False(parameters.RawBodyData.ContainsKey("customCoordinates")); + Assert.Null(parameters.CustomMetadata); + Assert.False(parameters.RawBodyData.ContainsKey("customMetadata")); + Assert.Null(parameters.Description); + Assert.False(parameters.RawBodyData.ContainsKey("description")); + Assert.Null(parameters.Extensions); + Assert.False(parameters.RawBodyData.ContainsKey("extensions")); + Assert.Null(parameters.Folder); + Assert.False(parameters.RawBodyData.ContainsKey("folder")); + Assert.Null(parameters.IsPrivateFile); + Assert.False(parameters.RawBodyData.ContainsKey("isPrivateFile")); + Assert.Null(parameters.IsPublished); + Assert.False(parameters.RawBodyData.ContainsKey("isPublished")); + Assert.Null(parameters.OverwriteAITags); + Assert.False(parameters.RawBodyData.ContainsKey("overwriteAITags")); + Assert.Null(parameters.OverwriteCustomMetadata); + Assert.False(parameters.RawBodyData.ContainsKey("overwriteCustomMetadata")); + Assert.Null(parameters.OverwriteFile); + Assert.False(parameters.RawBodyData.ContainsKey("overwriteFile")); + Assert.Null(parameters.OverwriteTags); + Assert.False(parameters.RawBodyData.ContainsKey("overwriteTags")); + Assert.Null(parameters.ResponseFields); + Assert.False(parameters.RawBodyData.ContainsKey("responseFields")); + Assert.Null(parameters.Tags); + Assert.False(parameters.RawBodyData.ContainsKey("tags")); + Assert.Null(parameters.Transformation); + Assert.False(parameters.RawBodyData.ContainsKey("transformation")); + Assert.Null(parameters.UseUniqueFileName); + Assert.False(parameters.RawBodyData.ContainsKey("useUniqueFileName")); + Assert.Null(parameters.WebhookUrl); + Assert.False(parameters.RawBodyData.ContainsKey("webhookUrl")); + } + + [Fact] + public void OptionalNonNullableParamsSetToNullAreNotSet_Works() + { + BinaryContent file = Encoding.UTF8.GetBytes("Example data"); + + var parameters = new FileUploadParams + { + File = file, + FileName = "fileName", + + // Null should be interpreted as omitted for these properties + Token = null, + Checks = null, + CustomCoordinates = null, + CustomMetadata = null, + Description = null, + Extensions = null, + Folder = null, + IsPrivateFile = null, + IsPublished = null, + OverwriteAITags = null, + OverwriteCustomMetadata = null, + OverwriteFile = null, + OverwriteTags = null, + ResponseFields = null, + Tags = null, + Transformation = null, + UseUniqueFileName = null, + WebhookUrl = null, + }; + + Assert.Null(parameters.Token); + Assert.False(parameters.RawBodyData.ContainsKey("token")); + Assert.Null(parameters.Checks); + Assert.False(parameters.RawBodyData.ContainsKey("checks")); + Assert.Null(parameters.CustomCoordinates); + Assert.False(parameters.RawBodyData.ContainsKey("customCoordinates")); + Assert.Null(parameters.CustomMetadata); + Assert.False(parameters.RawBodyData.ContainsKey("customMetadata")); + Assert.Null(parameters.Description); + Assert.False(parameters.RawBodyData.ContainsKey("description")); + Assert.Null(parameters.Extensions); + Assert.False(parameters.RawBodyData.ContainsKey("extensions")); + Assert.Null(parameters.Folder); + Assert.False(parameters.RawBodyData.ContainsKey("folder")); + Assert.Null(parameters.IsPrivateFile); + Assert.False(parameters.RawBodyData.ContainsKey("isPrivateFile")); + Assert.Null(parameters.IsPublished); + Assert.False(parameters.RawBodyData.ContainsKey("isPublished")); + Assert.Null(parameters.OverwriteAITags); + Assert.False(parameters.RawBodyData.ContainsKey("overwriteAITags")); + Assert.Null(parameters.OverwriteCustomMetadata); + Assert.False(parameters.RawBodyData.ContainsKey("overwriteCustomMetadata")); + Assert.Null(parameters.OverwriteFile); + Assert.False(parameters.RawBodyData.ContainsKey("overwriteFile")); + Assert.Null(parameters.OverwriteTags); + Assert.False(parameters.RawBodyData.ContainsKey("overwriteTags")); + Assert.Null(parameters.ResponseFields); + Assert.False(parameters.RawBodyData.ContainsKey("responseFields")); + Assert.Null(parameters.Tags); + Assert.False(parameters.RawBodyData.ContainsKey("tags")); + Assert.Null(parameters.Transformation); + Assert.False(parameters.RawBodyData.ContainsKey("transformation")); + Assert.Null(parameters.UseUniqueFileName); + Assert.False(parameters.RawBodyData.ContainsKey("useUniqueFileName")); + Assert.Null(parameters.WebhookUrl); + Assert.False(parameters.RawBodyData.ContainsKey("webhookUrl")); + } + + [Fact] + public void Url_Works() + { + FileUploadParams parameters = new() + { + File = Encoding.UTF8.GetBytes("Example data"), + FileName = "fileName", + }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/api/v2/files/upload"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new FileUploadParams + { + File = Encoding.UTF8.GetBytes("Example data"), + FileName = "fileName", + Token = "token", + Checks = "\"request.folder\" : \"marketing/\"\n", + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "brand", JsonSerializer.SerializeToElement("bar") }, + { "color", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "Running shoes", + Extensions = + [ + new Models::ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new Models::ExtensionItemAutoTaggingExtension() + { + MaxTags = 5, + MinConfidence = 95, + Name = Models::ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new Models::ExtensionItemAIAutoDescription(), + new Models::ExtensionItemAITasks( + [ + new Models::ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + new Models::ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }, + ] + ), + new Models::SavedExtension("ext_abc123"), + ], + Folder = "folder", + IsPrivateFile = true, + IsPublished = true, + OverwriteAITags = true, + OverwriteCustomMetadata = true, + OverwriteFile = true, + OverwriteTags = true, + ResponseFields = + [ + ResponseField.Tags, + ResponseField.CustomCoordinates, + ResponseField.IsPrivateFile, + ], + Tags = ["t-shirt", "round-neck", "men"], + Transformation = new() + { + Post = + [ + new Thumbnail() { Value = "w-150,h-150" }, + new Abs() { Protocol = Protocol.Dash, Value = "sr-240_360_480_720_1080" }, + ], + Pre = "w-300,h-300,q-80", + }, + UseUniqueFileName = true, + WebhookUrl = "https://example.com", + }; + + FileUploadParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} + +public class ResponseFieldTest : TestBase +{ + [Theory] + [InlineData(ResponseField.Tags)] + [InlineData(ResponseField.CustomCoordinates)] + [InlineData(ResponseField.IsPrivateFile)] + [InlineData(ResponseField.EmbeddedMetadata)] + [InlineData(ResponseField.IsPublished)] + [InlineData(ResponseField.CustomMetadata)] + [InlineData(ResponseField.Metadata)] + [InlineData(ResponseField.SelectedFieldsSchema)] + public void Validation_Works(ResponseField rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(ResponseField.Tags)] + [InlineData(ResponseField.CustomCoordinates)] + [InlineData(ResponseField.IsPrivateFile)] + [InlineData(ResponseField.EmbeddedMetadata)] + [InlineData(ResponseField.IsPublished)] + [InlineData(ResponseField.CustomMetadata)] + [InlineData(ResponseField.Metadata)] + [InlineData(ResponseField.SelectedFieldsSchema)] + public void SerializationRoundtrip_Works(ResponseField rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class TransformationTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Transformation + { + Post = + [ + new Thumbnail() { Value = "w-150,h-150" }, + new Abs() { Protocol = Protocol.Dash, Value = "sr-240_360_480_720_1080" }, + ], + Pre = "w-300,h-300,q-80", + }; + + List expectedPost = + [ + new Thumbnail() { Value = "w-150,h-150" }, + new Abs() { Protocol = Protocol.Dash, Value = "sr-240_360_480_720_1080" }, + ]; + string expectedPre = "w-300,h-300,q-80"; + + Assert.NotNull(model.Post); + Assert.Equal(expectedPost.Count, model.Post.Count); + for (int i = 0; i < expectedPost.Count; i++) + { + Assert.Equal(expectedPost[i], model.Post[i]); + } + Assert.Equal(expectedPre, model.Pre); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Transformation + { + Post = + [ + new Thumbnail() { Value = "w-150,h-150" }, + new Abs() { Protocol = Protocol.Dash, Value = "sr-240_360_480_720_1080" }, + ], + Pre = "w-300,h-300,q-80", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Transformation + { + Post = + [ + new Thumbnail() { Value = "w-150,h-150" }, + new Abs() { Protocol = Protocol.Dash, Value = "sr-240_360_480_720_1080" }, + ], + Pre = "w-300,h-300,q-80", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + List expectedPost = + [ + new Thumbnail() { Value = "w-150,h-150" }, + new Abs() { Protocol = Protocol.Dash, Value = "sr-240_360_480_720_1080" }, + ]; + string expectedPre = "w-300,h-300,q-80"; + + Assert.NotNull(deserialized.Post); + Assert.Equal(expectedPost.Count, deserialized.Post.Count); + for (int i = 0; i < expectedPost.Count; i++) + { + Assert.Equal(expectedPost[i], deserialized.Post[i]); + } + Assert.Equal(expectedPre, deserialized.Pre); + } + + [Fact] + public void Validation_Works() + { + var model = new Transformation + { + Post = + [ + new Thumbnail() { Value = "w-150,h-150" }, + new Abs() { Protocol = Protocol.Dash, Value = "sr-240_360_480_720_1080" }, + ], + Pre = "w-300,h-300,q-80", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Transformation { }; + + Assert.Null(model.Post); + Assert.False(model.RawData.ContainsKey("post")); + Assert.Null(model.Pre); + Assert.False(model.RawData.ContainsKey("pre")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Transformation { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Transformation + { + // Null should be interpreted as omitted for these properties + Post = null, + Pre = null, + }; + + Assert.Null(model.Post); + Assert.False(model.RawData.ContainsKey("post")); + Assert.Null(model.Pre); + Assert.False(model.RawData.ContainsKey("pre")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Transformation + { + // Null should be interpreted as omitted for these properties + Post = null, + Pre = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Transformation + { + Post = + [ + new Thumbnail() { Value = "w-150,h-150" }, + new Abs() { Protocol = Protocol.Dash, Value = "sr-240_360_480_720_1080" }, + ], + Pre = "w-300,h-300,q-80", + }; + + Transformation copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class PostTest : TestBase +{ + [Fact] + public void TransformationValidationWorks() + { + Post value = new PostTransformation("w-400,h-400,q-70"); + value.Validate(); + } + + [Fact] + public void GifToVideoValidationWorks() + { + Post value = new GifToVideo() { Value = "q-90" }; + value.Validate(); + } + + [Fact] + public void ThumbnailValidationWorks() + { + Post value = new Thumbnail() { Value = "w-150,h-150" }; + value.Validate(); + } + + [Fact] + public void AbsValidationWorks() + { + Post value = new Abs() { Protocol = Protocol.Hls, Value = "sr-240_360_480_720_1080" }; + value.Validate(); + } + + [Fact] + public void TransformationSerializationRoundtripWorks() + { + Post value = new PostTransformation("w-400,h-400,q-70"); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void GifToVideoSerializationRoundtripWorks() + { + Post value = new GifToVideo() { Value = "q-90" }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void ThumbnailSerializationRoundtripWorks() + { + Post value = new Thumbnail() { Value = "w-150,h-150" }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void AbsSerializationRoundtripWorks() + { + Post value = new Abs() { Protocol = Protocol.Hls, Value = "sr-240_360_480_720_1080" }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class PostTransformationTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new PostTransformation { ValueValue = "w-400,h-400,q-70" }; + + JsonElement expectedType = JsonSerializer.SerializeToElement("transformation"); + string expectedValueValue = "w-400,h-400,q-70"; + + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedValueValue, model.ValueValue); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new PostTransformation { ValueValue = "w-400,h-400,q-70" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new PostTransformation { ValueValue = "w-400,h-400,q-70" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + JsonElement expectedType = JsonSerializer.SerializeToElement("transformation"); + string expectedValueValue = "w-400,h-400,q-70"; + + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedValueValue, deserialized.ValueValue); + } + + [Fact] + public void Validation_Works() + { + var model = new PostTransformation { ValueValue = "w-400,h-400,q-70" }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new PostTransformation { ValueValue = "w-400,h-400,q-70" }; + + PostTransformation copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class GifToVideoTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new GifToVideo { Value = "q-90" }; + + JsonElement expectedType = JsonSerializer.SerializeToElement("gif-to-video"); + string expectedValue = "q-90"; + + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedValue, model.Value); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new GifToVideo { Value = "q-90" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new GifToVideo { Value = "q-90" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + JsonElement expectedType = JsonSerializer.SerializeToElement("gif-to-video"); + string expectedValue = "q-90"; + + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedValue, deserialized.Value); + } + + [Fact] + public void Validation_Works() + { + var model = new GifToVideo { Value = "q-90" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new GifToVideo { }; + + Assert.Null(model.Value); + Assert.False(model.RawData.ContainsKey("value")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new GifToVideo { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new GifToVideo + { + // Null should be interpreted as omitted for these properties + Value = null, + }; + + Assert.Null(model.Value); + Assert.False(model.RawData.ContainsKey("value")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new GifToVideo + { + // Null should be interpreted as omitted for these properties + Value = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new GifToVideo { Value = "q-90" }; + + GifToVideo copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ThumbnailTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Thumbnail { Value = "w-150,h-150" }; + + JsonElement expectedType = JsonSerializer.SerializeToElement("thumbnail"); + string expectedValue = "w-150,h-150"; + + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedValue, model.Value); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Thumbnail { Value = "w-150,h-150" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Thumbnail { Value = "w-150,h-150" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + JsonElement expectedType = JsonSerializer.SerializeToElement("thumbnail"); + string expectedValue = "w-150,h-150"; + + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedValue, deserialized.Value); + } + + [Fact] + public void Validation_Works() + { + var model = new Thumbnail { Value = "w-150,h-150" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Thumbnail { }; + + Assert.Null(model.Value); + Assert.False(model.RawData.ContainsKey("value")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Thumbnail { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Thumbnail + { + // Null should be interpreted as omitted for these properties + Value = null, + }; + + Assert.Null(model.Value); + Assert.False(model.RawData.ContainsKey("value")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Thumbnail + { + // Null should be interpreted as omitted for these properties + Value = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Thumbnail { Value = "w-150,h-150" }; + + Thumbnail copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class AbsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Abs { Protocol = Protocol.Hls, Value = "sr-240_360_480_720_1080" }; + + ApiEnum expectedProtocol = Protocol.Hls; + JsonElement expectedType = JsonSerializer.SerializeToElement("abs"); + string expectedValue = "sr-240_360_480_720_1080"; + + Assert.Equal(expectedProtocol, model.Protocol); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedValue, model.Value); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Abs { Protocol = Protocol.Hls, Value = "sr-240_360_480_720_1080" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Abs { Protocol = Protocol.Hls, Value = "sr-240_360_480_720_1080" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + Assert.NotNull(deserialized); + + ApiEnum expectedProtocol = Protocol.Hls; + JsonElement expectedType = JsonSerializer.SerializeToElement("abs"); + string expectedValue = "sr-240_360_480_720_1080"; + + Assert.Equal(expectedProtocol, deserialized.Protocol); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedValue, deserialized.Value); + } + + [Fact] + public void Validation_Works() + { + var model = new Abs { Protocol = Protocol.Hls, Value = "sr-240_360_480_720_1080" }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Abs { Protocol = Protocol.Hls, Value = "sr-240_360_480_720_1080" }; + + Abs copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ProtocolTest : TestBase +{ + [Theory] + [InlineData(Protocol.Hls)] + [InlineData(Protocol.Dash)] + public void Validation_Works(Protocol rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Protocol.Hls)] + [InlineData(Protocol.Dash)] + public void SerializationRoundtrip_Works(Protocol rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/Beta/V2/Files/FileUploadResponseTest.cs b/src/Imagekit.Tests/Models/Beta/V2/Files/FileUploadResponseTest.cs new file mode 100644 index 00000000..1b1a6930 --- /dev/null +++ b/src/Imagekit.Tests/Models/Beta/V2/Files/FileUploadResponseTest.cs @@ -0,0 +1,2556 @@ +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models; +using Files = Imagekit.Models.Beta.V2.Files; +using ModelsFiles = Imagekit.Models.Files; + +namespace Imagekit.Tests.Models.Beta.V2.Files; + +public class FileUploadResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Files::FileUploadResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + List expectedAITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ]; + string expectedAudioCodec = "audioCodec"; + long expectedBitRate = 0; + string expectedCustomCoordinates = "customCoordinates"; + Dictionary expectedCustomMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + string expectedDescription = "description"; + long expectedDuration = 0; + Dictionary expectedEmbeddedMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + Files::ExtensionStatus expectedExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }; + string expectedFileID = "fileId"; + string expectedFilePath = "filePath"; + string expectedFileType = "fileType"; + double expectedHeight = 0; + bool expectedIsPrivateFile = true; + bool expectedIsPublished = true; + ModelsFiles::FileMetadata expectedMetadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }; + string expectedName = "name"; + Dictionary expectedSelectedFieldsSchema = new() + { + { + "foo", + new() + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }; + double expectedSize = 0; + List expectedTags = ["string"]; + string expectedThumbnailUrl = "thumbnailUrl"; + string expectedUrl = "url"; + VersionInfo expectedVersionInfo = new() { ID = "id", Name = "name" }; + string expectedVideoCodec = "videoCodec"; + double expectedWidth = 0; + + Assert.NotNull(model.AITags); + Assert.Equal(expectedAITags.Count, model.AITags.Count); + for (int i = 0; i < expectedAITags.Count; i++) + { + Assert.Equal(expectedAITags[i], model.AITags[i]); + } + Assert.Equal(expectedAudioCodec, model.AudioCodec); + Assert.Equal(expectedBitRate, model.BitRate); + Assert.Equal(expectedCustomCoordinates, model.CustomCoordinates); + Assert.NotNull(model.CustomMetadata); + Assert.Equal(expectedCustomMetadata.Count, model.CustomMetadata.Count); + foreach (var item in expectedCustomMetadata) + { + Assert.True(model.CustomMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, model.CustomMetadata[item.Key])); + } + Assert.Equal(expectedDescription, model.Description); + Assert.Equal(expectedDuration, model.Duration); + Assert.NotNull(model.EmbeddedMetadata); + Assert.Equal(expectedEmbeddedMetadata.Count, model.EmbeddedMetadata.Count); + foreach (var item in expectedEmbeddedMetadata) + { + Assert.True(model.EmbeddedMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, model.EmbeddedMetadata[item.Key])); + } + Assert.Equal(expectedExtensionStatus, model.ExtensionStatus); + Assert.Equal(expectedFileID, model.FileID); + Assert.Equal(expectedFilePath, model.FilePath); + Assert.Equal(expectedFileType, model.FileType); + Assert.Equal(expectedHeight, model.Height); + Assert.Equal(expectedIsPrivateFile, model.IsPrivateFile); + Assert.Equal(expectedIsPublished, model.IsPublished); + Assert.Equal(expectedMetadata, model.Metadata); + Assert.Equal(expectedName, model.Name); + Assert.NotNull(model.SelectedFieldsSchema); + Assert.Equal(expectedSelectedFieldsSchema.Count, model.SelectedFieldsSchema.Count); + foreach (var item in expectedSelectedFieldsSchema) + { + Assert.True(model.SelectedFieldsSchema.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.SelectedFieldsSchema[item.Key]); + } + Assert.Equal(expectedSize, model.Size); + Assert.NotNull(model.Tags); + Assert.Equal(expectedTags.Count, model.Tags.Count); + for (int i = 0; i < expectedTags.Count; i++) + { + Assert.Equal(expectedTags[i], model.Tags[i]); + } + Assert.Equal(expectedThumbnailUrl, model.ThumbnailUrl); + Assert.Equal(expectedUrl, model.Url); + Assert.Equal(expectedVersionInfo, model.VersionInfo); + Assert.Equal(expectedVideoCodec, model.VideoCodec); + Assert.Equal(expectedWidth, model.Width); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Files::FileUploadResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Files::FileUploadResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + List expectedAITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ]; + string expectedAudioCodec = "audioCodec"; + long expectedBitRate = 0; + string expectedCustomCoordinates = "customCoordinates"; + Dictionary expectedCustomMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + string expectedDescription = "description"; + long expectedDuration = 0; + Dictionary expectedEmbeddedMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + Files::ExtensionStatus expectedExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }; + string expectedFileID = "fileId"; + string expectedFilePath = "filePath"; + string expectedFileType = "fileType"; + double expectedHeight = 0; + bool expectedIsPrivateFile = true; + bool expectedIsPublished = true; + ModelsFiles::FileMetadata expectedMetadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }; + string expectedName = "name"; + Dictionary expectedSelectedFieldsSchema = new() + { + { + "foo", + new() + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }; + double expectedSize = 0; + List expectedTags = ["string"]; + string expectedThumbnailUrl = "thumbnailUrl"; + string expectedUrl = "url"; + VersionInfo expectedVersionInfo = new() { ID = "id", Name = "name" }; + string expectedVideoCodec = "videoCodec"; + double expectedWidth = 0; + + Assert.NotNull(deserialized.AITags); + Assert.Equal(expectedAITags.Count, deserialized.AITags.Count); + for (int i = 0; i < expectedAITags.Count; i++) + { + Assert.Equal(expectedAITags[i], deserialized.AITags[i]); + } + Assert.Equal(expectedAudioCodec, deserialized.AudioCodec); + Assert.Equal(expectedBitRate, deserialized.BitRate); + Assert.Equal(expectedCustomCoordinates, deserialized.CustomCoordinates); + Assert.NotNull(deserialized.CustomMetadata); + Assert.Equal(expectedCustomMetadata.Count, deserialized.CustomMetadata.Count); + foreach (var item in expectedCustomMetadata) + { + Assert.True(deserialized.CustomMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, deserialized.CustomMetadata[item.Key])); + } + Assert.Equal(expectedDescription, deserialized.Description); + Assert.Equal(expectedDuration, deserialized.Duration); + Assert.NotNull(deserialized.EmbeddedMetadata); + Assert.Equal(expectedEmbeddedMetadata.Count, deserialized.EmbeddedMetadata.Count); + foreach (var item in expectedEmbeddedMetadata) + { + Assert.True(deserialized.EmbeddedMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, deserialized.EmbeddedMetadata[item.Key])); + } + Assert.Equal(expectedExtensionStatus, deserialized.ExtensionStatus); + Assert.Equal(expectedFileID, deserialized.FileID); + Assert.Equal(expectedFilePath, deserialized.FilePath); + Assert.Equal(expectedFileType, deserialized.FileType); + Assert.Equal(expectedHeight, deserialized.Height); + Assert.Equal(expectedIsPrivateFile, deserialized.IsPrivateFile); + Assert.Equal(expectedIsPublished, deserialized.IsPublished); + Assert.Equal(expectedMetadata, deserialized.Metadata); + Assert.Equal(expectedName, deserialized.Name); + Assert.NotNull(deserialized.SelectedFieldsSchema); + Assert.Equal(expectedSelectedFieldsSchema.Count, deserialized.SelectedFieldsSchema.Count); + foreach (var item in expectedSelectedFieldsSchema) + { + Assert.True(deserialized.SelectedFieldsSchema.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.SelectedFieldsSchema[item.Key]); + } + Assert.Equal(expectedSize, deserialized.Size); + Assert.NotNull(deserialized.Tags); + Assert.Equal(expectedTags.Count, deserialized.Tags.Count); + for (int i = 0; i < expectedTags.Count; i++) + { + Assert.Equal(expectedTags[i], deserialized.Tags[i]); + } + Assert.Equal(expectedThumbnailUrl, deserialized.ThumbnailUrl); + Assert.Equal(expectedUrl, deserialized.Url); + Assert.Equal(expectedVersionInfo, deserialized.VersionInfo); + Assert.Equal(expectedVideoCodec, deserialized.VideoCodec); + Assert.Equal(expectedWidth, deserialized.Width); + } + + [Fact] + public void Validation_Works() + { + var model = new Files::FileUploadResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Files::FileUploadResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + CustomCoordinates = "customCoordinates", + Tags = ["string"], + }; + + Assert.Null(model.AudioCodec); + Assert.False(model.RawData.ContainsKey("audioCodec")); + Assert.Null(model.BitRate); + Assert.False(model.RawData.ContainsKey("bitRate")); + Assert.Null(model.CustomMetadata); + Assert.False(model.RawData.ContainsKey("customMetadata")); + Assert.Null(model.Description); + Assert.False(model.RawData.ContainsKey("description")); + Assert.Null(model.Duration); + Assert.False(model.RawData.ContainsKey("duration")); + Assert.Null(model.EmbeddedMetadata); + Assert.False(model.RawData.ContainsKey("embeddedMetadata")); + Assert.Null(model.ExtensionStatus); + Assert.False(model.RawData.ContainsKey("extensionStatus")); + Assert.Null(model.FileID); + Assert.False(model.RawData.ContainsKey("fileId")); + Assert.Null(model.FilePath); + Assert.False(model.RawData.ContainsKey("filePath")); + Assert.Null(model.FileType); + Assert.False(model.RawData.ContainsKey("fileType")); + Assert.Null(model.Height); + Assert.False(model.RawData.ContainsKey("height")); + Assert.Null(model.IsPrivateFile); + Assert.False(model.RawData.ContainsKey("isPrivateFile")); + Assert.Null(model.IsPublished); + Assert.False(model.RawData.ContainsKey("isPublished")); + Assert.Null(model.Metadata); + Assert.False(model.RawData.ContainsKey("metadata")); + Assert.Null(model.Name); + Assert.False(model.RawData.ContainsKey("name")); + Assert.Null(model.SelectedFieldsSchema); + Assert.False(model.RawData.ContainsKey("selectedFieldsSchema")); + Assert.Null(model.Size); + Assert.False(model.RawData.ContainsKey("size")); + Assert.Null(model.ThumbnailUrl); + Assert.False(model.RawData.ContainsKey("thumbnailUrl")); + Assert.Null(model.Url); + Assert.False(model.RawData.ContainsKey("url")); + Assert.Null(model.VersionInfo); + Assert.False(model.RawData.ContainsKey("versionInfo")); + Assert.Null(model.VideoCodec); + Assert.False(model.RawData.ContainsKey("videoCodec")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Files::FileUploadResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + CustomCoordinates = "customCoordinates", + Tags = ["string"], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Files::FileUploadResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + CustomCoordinates = "customCoordinates", + Tags = ["string"], + + // Null should be interpreted as omitted for these properties + AudioCodec = null, + BitRate = null, + CustomMetadata = null, + Description = null, + Duration = null, + EmbeddedMetadata = null, + ExtensionStatus = null, + FileID = null, + FilePath = null, + FileType = null, + Height = null, + IsPrivateFile = null, + IsPublished = null, + Metadata = null, + Name = null, + SelectedFieldsSchema = null, + Size = null, + ThumbnailUrl = null, + Url = null, + VersionInfo = null, + VideoCodec = null, + Width = null, + }; + + Assert.Null(model.AudioCodec); + Assert.False(model.RawData.ContainsKey("audioCodec")); + Assert.Null(model.BitRate); + Assert.False(model.RawData.ContainsKey("bitRate")); + Assert.Null(model.CustomMetadata); + Assert.False(model.RawData.ContainsKey("customMetadata")); + Assert.Null(model.Description); + Assert.False(model.RawData.ContainsKey("description")); + Assert.Null(model.Duration); + Assert.False(model.RawData.ContainsKey("duration")); + Assert.Null(model.EmbeddedMetadata); + Assert.False(model.RawData.ContainsKey("embeddedMetadata")); + Assert.Null(model.ExtensionStatus); + Assert.False(model.RawData.ContainsKey("extensionStatus")); + Assert.Null(model.FileID); + Assert.False(model.RawData.ContainsKey("fileId")); + Assert.Null(model.FilePath); + Assert.False(model.RawData.ContainsKey("filePath")); + Assert.Null(model.FileType); + Assert.False(model.RawData.ContainsKey("fileType")); + Assert.Null(model.Height); + Assert.False(model.RawData.ContainsKey("height")); + Assert.Null(model.IsPrivateFile); + Assert.False(model.RawData.ContainsKey("isPrivateFile")); + Assert.Null(model.IsPublished); + Assert.False(model.RawData.ContainsKey("isPublished")); + Assert.Null(model.Metadata); + Assert.False(model.RawData.ContainsKey("metadata")); + Assert.Null(model.Name); + Assert.False(model.RawData.ContainsKey("name")); + Assert.Null(model.SelectedFieldsSchema); + Assert.False(model.RawData.ContainsKey("selectedFieldsSchema")); + Assert.Null(model.Size); + Assert.False(model.RawData.ContainsKey("size")); + Assert.Null(model.ThumbnailUrl); + Assert.False(model.RawData.ContainsKey("thumbnailUrl")); + Assert.Null(model.Url); + Assert.False(model.RawData.ContainsKey("url")); + Assert.Null(model.VersionInfo); + Assert.False(model.RawData.ContainsKey("versionInfo")); + Assert.Null(model.VideoCodec); + Assert.False(model.RawData.ContainsKey("videoCodec")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Files::FileUploadResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + CustomCoordinates = "customCoordinates", + Tags = ["string"], + + // Null should be interpreted as omitted for these properties + AudioCodec = null, + BitRate = null, + CustomMetadata = null, + Description = null, + Duration = null, + EmbeddedMetadata = null, + ExtensionStatus = null, + FileID = null, + FilePath = null, + FileType = null, + Height = null, + IsPrivateFile = null, + IsPublished = null, + Metadata = null, + Name = null, + SelectedFieldsSchema = null, + Size = null, + ThumbnailUrl = null, + Url = null, + VersionInfo = null, + VideoCodec = null, + Width = null, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Files::FileUploadResponse + { + AudioCodec = "audioCodec", + BitRate = 0, + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + Assert.Null(model.AITags); + Assert.False(model.RawData.ContainsKey("AITags")); + Assert.Null(model.CustomCoordinates); + Assert.False(model.RawData.ContainsKey("customCoordinates")); + Assert.Null(model.Tags); + Assert.False(model.RawData.ContainsKey("tags")); + } + + [Fact] + public void OptionalNullablePropertiesUnsetValidation_Works() + { + var model = new Files::FileUploadResponse + { + AudioCodec = "audioCodec", + BitRate = 0, + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNullablePropertiesSetToNullAreSetToNull_Works() + { + var model = new Files::FileUploadResponse + { + AudioCodec = "audioCodec", + BitRate = 0, + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + + AITags = null, + CustomCoordinates = null, + Tags = null, + }; + + Assert.Null(model.AITags); + Assert.True(model.RawData.ContainsKey("AITags")); + Assert.Null(model.CustomCoordinates); + Assert.True(model.RawData.ContainsKey("customCoordinates")); + Assert.Null(model.Tags); + Assert.True(model.RawData.ContainsKey("tags")); + } + + [Fact] + public void OptionalNullablePropertiesSetToNullValidation_Works() + { + var model = new Files::FileUploadResponse + { + AudioCodec = "audioCodec", + BitRate = 0, + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + + AITags = null, + CustomCoordinates = null, + Tags = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Files::FileUploadResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + Files::FileUploadResponse copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExtensionStatusTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Files::ExtensionStatus + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }; + + ApiEnum expectedAIAutoDescription = + Files::AIAutoDescription.Success; + ApiEnum expectedAITasks = Files::AITasks.Success; + ApiEnum expectedAwsAutoTagging = + Files::AwsAutoTagging.Success; + ApiEnum expectedGoogleAutoTagging = + Files::GoogleAutoTagging.Success; + ApiEnum expectedRemoveBg = Files::RemoveBg.Success; + + Assert.Equal(expectedAIAutoDescription, model.AIAutoDescription); + Assert.Equal(expectedAITasks, model.AITasks); + Assert.Equal(expectedAwsAutoTagging, model.AwsAutoTagging); + Assert.Equal(expectedGoogleAutoTagging, model.GoogleAutoTagging); + Assert.Equal(expectedRemoveBg, model.RemoveBg); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Files::ExtensionStatus + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Files::ExtensionStatus + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum expectedAIAutoDescription = + Files::AIAutoDescription.Success; + ApiEnum expectedAITasks = Files::AITasks.Success; + ApiEnum expectedAwsAutoTagging = + Files::AwsAutoTagging.Success; + ApiEnum expectedGoogleAutoTagging = + Files::GoogleAutoTagging.Success; + ApiEnum expectedRemoveBg = Files::RemoveBg.Success; + + Assert.Equal(expectedAIAutoDescription, deserialized.AIAutoDescription); + Assert.Equal(expectedAITasks, deserialized.AITasks); + Assert.Equal(expectedAwsAutoTagging, deserialized.AwsAutoTagging); + Assert.Equal(expectedGoogleAutoTagging, deserialized.GoogleAutoTagging); + Assert.Equal(expectedRemoveBg, deserialized.RemoveBg); + } + + [Fact] + public void Validation_Works() + { + var model = new Files::ExtensionStatus + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Files::ExtensionStatus { }; + + Assert.Null(model.AIAutoDescription); + Assert.False(model.RawData.ContainsKey("ai-auto-description")); + Assert.Null(model.AITasks); + Assert.False(model.RawData.ContainsKey("ai-tasks")); + Assert.Null(model.AwsAutoTagging); + Assert.False(model.RawData.ContainsKey("aws-auto-tagging")); + Assert.Null(model.GoogleAutoTagging); + Assert.False(model.RawData.ContainsKey("google-auto-tagging")); + Assert.Null(model.RemoveBg); + Assert.False(model.RawData.ContainsKey("remove-bg")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Files::ExtensionStatus { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Files::ExtensionStatus + { + // Null should be interpreted as omitted for these properties + AIAutoDescription = null, + AITasks = null, + AwsAutoTagging = null, + GoogleAutoTagging = null, + RemoveBg = null, + }; + + Assert.Null(model.AIAutoDescription); + Assert.False(model.RawData.ContainsKey("ai-auto-description")); + Assert.Null(model.AITasks); + Assert.False(model.RawData.ContainsKey("ai-tasks")); + Assert.Null(model.AwsAutoTagging); + Assert.False(model.RawData.ContainsKey("aws-auto-tagging")); + Assert.Null(model.GoogleAutoTagging); + Assert.False(model.RawData.ContainsKey("google-auto-tagging")); + Assert.Null(model.RemoveBg); + Assert.False(model.RawData.ContainsKey("remove-bg")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Files::ExtensionStatus + { + // Null should be interpreted as omitted for these properties + AIAutoDescription = null, + AITasks = null, + AwsAutoTagging = null, + GoogleAutoTagging = null, + RemoveBg = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Files::ExtensionStatus + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }; + + Files::ExtensionStatus copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class AIAutoDescriptionTest : TestBase +{ + [Theory] + [InlineData(Files::AIAutoDescription.Success)] + [InlineData(Files::AIAutoDescription.Pending)] + [InlineData(Files::AIAutoDescription.Failed)] + public void Validation_Works(Files::AIAutoDescription rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Files::AIAutoDescription.Success)] + [InlineData(Files::AIAutoDescription.Pending)] + [InlineData(Files::AIAutoDescription.Failed)] + public void SerializationRoundtrip_Works(Files::AIAutoDescription rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class AITasksTest : TestBase +{ + [Theory] + [InlineData(Files::AITasks.Success)] + [InlineData(Files::AITasks.Pending)] + [InlineData(Files::AITasks.Failed)] + public void Validation_Works(Files::AITasks rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Files::AITasks.Success)] + [InlineData(Files::AITasks.Pending)] + [InlineData(Files::AITasks.Failed)] + public void SerializationRoundtrip_Works(Files::AITasks rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class AwsAutoTaggingTest : TestBase +{ + [Theory] + [InlineData(Files::AwsAutoTagging.Success)] + [InlineData(Files::AwsAutoTagging.Pending)] + [InlineData(Files::AwsAutoTagging.Failed)] + public void Validation_Works(Files::AwsAutoTagging rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Files::AwsAutoTagging.Success)] + [InlineData(Files::AwsAutoTagging.Pending)] + [InlineData(Files::AwsAutoTagging.Failed)] + public void SerializationRoundtrip_Works(Files::AwsAutoTagging rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class GoogleAutoTaggingTest : TestBase +{ + [Theory] + [InlineData(Files::GoogleAutoTagging.Success)] + [InlineData(Files::GoogleAutoTagging.Pending)] + [InlineData(Files::GoogleAutoTagging.Failed)] + public void Validation_Works(Files::GoogleAutoTagging rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Files::GoogleAutoTagging.Success)] + [InlineData(Files::GoogleAutoTagging.Pending)] + [InlineData(Files::GoogleAutoTagging.Failed)] + public void SerializationRoundtrip_Works(Files::GoogleAutoTagging rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class RemoveBgTest : TestBase +{ + [Theory] + [InlineData(Files::RemoveBg.Success)] + [InlineData(Files::RemoveBg.Pending)] + [InlineData(Files::RemoveBg.Failed)] + public void Validation_Works(Files::RemoveBg rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Files::RemoveBg.Success)] + [InlineData(Files::RemoveBg.Pending)] + [InlineData(Files::RemoveBg.Failed)] + public void SerializationRoundtrip_Works(Files::RemoveBg rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/Cache/Invalidation/InvalidationCreateParamsTest.cs b/src/Imagekit.Tests/Models/Cache/Invalidation/InvalidationCreateParamsTest.cs new file mode 100644 index 00000000..f3fe4260 --- /dev/null +++ b/src/Imagekit.Tests/Models/Cache/Invalidation/InvalidationCreateParamsTest.cs @@ -0,0 +1,46 @@ +using System; +using Imagekit.Models.Cache.Invalidation; + +namespace Imagekit.Tests.Models.Cache.Invalidation; + +public class InvalidationCreateParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new InvalidationCreateParams + { + UrlValue = "https://ik.imagekit.io/your_imagekit_id/default-image.jpg", + }; + + string expectedUrlValue = "https://ik.imagekit.io/your_imagekit_id/default-image.jpg"; + + Assert.Equal(expectedUrlValue, parameters.UrlValue); + } + + [Fact] + public void Url_Works() + { + InvalidationCreateParams parameters = new() + { + UrlValue = "https://ik.imagekit.io/your_imagekit_id/default-image.jpg", + }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True(TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/files/purge"), url)); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new InvalidationCreateParams + { + UrlValue = "https://ik.imagekit.io/your_imagekit_id/default-image.jpg", + }; + + InvalidationCreateParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Cache/Invalidation/InvalidationCreateResponseTest.cs b/src/Imagekit.Tests/Models/Cache/Invalidation/InvalidationCreateResponseTest.cs new file mode 100644 index 00000000..08f67e6b --- /dev/null +++ b/src/Imagekit.Tests/Models/Cache/Invalidation/InvalidationCreateResponseTest.cs @@ -0,0 +1,109 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Cache.Invalidation; + +namespace Imagekit.Tests.Models.Cache.Invalidation; + +public class InvalidationCreateResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new InvalidationCreateResponse { RequestID = "requestId" }; + + string expectedRequestID = "requestId"; + + Assert.Equal(expectedRequestID, model.RequestID); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new InvalidationCreateResponse { RequestID = "requestId" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new InvalidationCreateResponse { RequestID = "requestId" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedRequestID = "requestId"; + + Assert.Equal(expectedRequestID, deserialized.RequestID); + } + + [Fact] + public void Validation_Works() + { + var model = new InvalidationCreateResponse { RequestID = "requestId" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new InvalidationCreateResponse { }; + + Assert.Null(model.RequestID); + Assert.False(model.RawData.ContainsKey("requestId")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new InvalidationCreateResponse { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new InvalidationCreateResponse + { + // Null should be interpreted as omitted for these properties + RequestID = null, + }; + + Assert.Null(model.RequestID); + Assert.False(model.RawData.ContainsKey("requestId")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new InvalidationCreateResponse + { + // Null should be interpreted as omitted for these properties + RequestID = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new InvalidationCreateResponse { RequestID = "requestId" }; + + InvalidationCreateResponse copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Cache/Invalidation/InvalidationGetParamsTest.cs b/src/Imagekit.Tests/Models/Cache/Invalidation/InvalidationGetParamsTest.cs new file mode 100644 index 00000000..5d33a03b --- /dev/null +++ b/src/Imagekit.Tests/Models/Cache/Invalidation/InvalidationGetParamsTest.cs @@ -0,0 +1,39 @@ +using System; +using Imagekit.Models.Cache.Invalidation; + +namespace Imagekit.Tests.Models.Cache.Invalidation; + +public class InvalidationGetParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new InvalidationGetParams { RequestID = "requestId" }; + + string expectedRequestID = "requestId"; + + Assert.Equal(expectedRequestID, parameters.RequestID); + } + + [Fact] + public void Url_Works() + { + InvalidationGetParams parameters = new() { RequestID = "requestId" }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/files/purge/requestId"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new InvalidationGetParams { RequestID = "requestId" }; + + InvalidationGetParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Cache/Invalidation/InvalidationGetResponseTest.cs b/src/Imagekit.Tests/Models/Cache/Invalidation/InvalidationGetResponseTest.cs new file mode 100644 index 00000000..5d8b540f --- /dev/null +++ b/src/Imagekit.Tests/Models/Cache/Invalidation/InvalidationGetResponseTest.cs @@ -0,0 +1,168 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Cache.Invalidation; + +namespace Imagekit.Tests.Models.Cache.Invalidation; + +public class InvalidationGetResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new InvalidationGetResponse { Status = Status.Completed }; + + ApiEnum expectedStatus = Status.Completed; + + Assert.Equal(expectedStatus, model.Status); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new InvalidationGetResponse { Status = Status.Completed }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new InvalidationGetResponse { Status = Status.Completed }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum expectedStatus = Status.Completed; + + Assert.Equal(expectedStatus, deserialized.Status); + } + + [Fact] + public void Validation_Works() + { + var model = new InvalidationGetResponse { Status = Status.Completed }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new InvalidationGetResponse { }; + + Assert.Null(model.Status); + Assert.False(model.RawData.ContainsKey("status")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new InvalidationGetResponse { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new InvalidationGetResponse + { + // Null should be interpreted as omitted for these properties + Status = null, + }; + + Assert.Null(model.Status); + Assert.False(model.RawData.ContainsKey("status")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new InvalidationGetResponse + { + // Null should be interpreted as omitted for these properties + Status = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new InvalidationGetResponse { Status = Status.Completed }; + + InvalidationGetResponse copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class StatusTest : TestBase +{ + [Theory] + [InlineData(Status.Pending)] + [InlineData(Status.Completed)] + public void Validation_Works(Status rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Status.Pending)] + [InlineData(Status.Completed)] + public void SerializationRoundtrip_Works(Status rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/CustomMetadataFields/CustomMetadataFieldCreateParamsTest.cs b/src/Imagekit.Tests/Models/CustomMetadataFields/CustomMetadataFieldCreateParamsTest.cs new file mode 100644 index 00000000..68fb4fa3 --- /dev/null +++ b/src/Imagekit.Tests/Models/CustomMetadataFields/CustomMetadataFieldCreateParamsTest.cs @@ -0,0 +1,791 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using CustomMetadataFields = Imagekit.Models.CustomMetadataFields; + +namespace Imagekit.Tests.Models.CustomMetadataFields; + +public class CustomMetadataFieldCreateParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new CustomMetadataFields::CustomMetadataFieldCreateParams + { + Label = "price", + Name = "price", + Schema = new() + { + Type = CustomMetadataFields::Type.Number, + DefaultValue = new( + [ + new CustomMetadataFields::DefaultValueItem(true), + new CustomMetadataFields::DefaultValueItem(10), + new CustomMetadataFields::DefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = 3000, + MinLength = 0, + MinValue = 1000, + SelectOptions = ["small", "medium", "large", 30, 40, true], + }, + }; + + string expectedLabel = "price"; + string expectedName = "price"; + CustomMetadataFields::Schema expectedSchema = new() + { + Type = CustomMetadataFields::Type.Number, + DefaultValue = new( + [ + new CustomMetadataFields::DefaultValueItem(true), + new CustomMetadataFields::DefaultValueItem(10), + new CustomMetadataFields::DefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = 3000, + MinLength = 0, + MinValue = 1000, + SelectOptions = ["small", "medium", "large", 30, 40, true], + }; + + Assert.Equal(expectedLabel, parameters.Label); + Assert.Equal(expectedName, parameters.Name); + Assert.Equal(expectedSchema, parameters.Schema); + } + + [Fact] + public void Url_Works() + { + CustomMetadataFields::CustomMetadataFieldCreateParams parameters = new() + { + Label = "price", + Name = "price", + Schema = new() + { + Type = CustomMetadataFields::Type.Number, + DefaultValue = new( + [ + new CustomMetadataFields::DefaultValueItem(true), + new CustomMetadataFields::DefaultValueItem(10), + new CustomMetadataFields::DefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = 3000, + MinLength = 0, + MinValue = 1000, + SelectOptions = ["small", "medium", "large", 30, 40, true], + }, + }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/customMetadataFields"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new CustomMetadataFields::CustomMetadataFieldCreateParams + { + Label = "price", + Name = "price", + Schema = new() + { + Type = CustomMetadataFields::Type.Number, + DefaultValue = new( + [ + new CustomMetadataFields::DefaultValueItem(true), + new CustomMetadataFields::DefaultValueItem(10), + new CustomMetadataFields::DefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = 3000, + MinLength = 0, + MinValue = 1000, + SelectOptions = ["small", "medium", "large", 30, 40, true], + }, + }; + + CustomMetadataFields::CustomMetadataFieldCreateParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} + +public class SchemaTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new CustomMetadataFields::Schema + { + Type = CustomMetadataFields::Type.Text, + DefaultValue = new( + [ + new CustomMetadataFields::DefaultValueItem(true), + new CustomMetadataFields::DefaultValueItem(10), + new CustomMetadataFields::DefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + SelectOptions = ["small", "medium", "large", 30, 40, true], + }; + + ApiEnum expectedType = CustomMetadataFields::Type.Text; + CustomMetadataFields::DefaultValue expectedDefaultValue = new( + [ + new CustomMetadataFields::DefaultValueItem(true), + new CustomMetadataFields::DefaultValueItem(10), + new CustomMetadataFields::DefaultValueItem("Hello"), + ] + ); + bool expectedIsValueRequired = true; + double expectedMaxLength = 0; + CustomMetadataFields::MaxValue expectedMaxValue = "string"; + double expectedMinLength = 0; + CustomMetadataFields::MinValue expectedMinValue = "string"; + List expectedSelectOptions = + [ + "small", + "medium", + "large", + 30, + 40, + true, + ]; + + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedDefaultValue, model.DefaultValue); + Assert.Equal(expectedIsValueRequired, model.IsValueRequired); + Assert.Equal(expectedMaxLength, model.MaxLength); + Assert.Equal(expectedMaxValue, model.MaxValue); + Assert.Equal(expectedMinLength, model.MinLength); + Assert.Equal(expectedMinValue, model.MinValue); + Assert.NotNull(model.SelectOptions); + Assert.Equal(expectedSelectOptions.Count, model.SelectOptions.Count); + for (int i = 0; i < expectedSelectOptions.Count; i++) + { + Assert.Equal(expectedSelectOptions[i], model.SelectOptions[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new CustomMetadataFields::Schema + { + Type = CustomMetadataFields::Type.Text, + DefaultValue = new( + [ + new CustomMetadataFields::DefaultValueItem(true), + new CustomMetadataFields::DefaultValueItem(10), + new CustomMetadataFields::DefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + SelectOptions = ["small", "medium", "large", 30, 40, true], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new CustomMetadataFields::Schema + { + Type = CustomMetadataFields::Type.Text, + DefaultValue = new( + [ + new CustomMetadataFields::DefaultValueItem(true), + new CustomMetadataFields::DefaultValueItem(10), + new CustomMetadataFields::DefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + SelectOptions = ["small", "medium", "large", 30, 40, true], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum expectedType = CustomMetadataFields::Type.Text; + CustomMetadataFields::DefaultValue expectedDefaultValue = new( + [ + new CustomMetadataFields::DefaultValueItem(true), + new CustomMetadataFields::DefaultValueItem(10), + new CustomMetadataFields::DefaultValueItem("Hello"), + ] + ); + bool expectedIsValueRequired = true; + double expectedMaxLength = 0; + CustomMetadataFields::MaxValue expectedMaxValue = "string"; + double expectedMinLength = 0; + CustomMetadataFields::MinValue expectedMinValue = "string"; + List expectedSelectOptions = + [ + "small", + "medium", + "large", + 30, + 40, + true, + ]; + + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedDefaultValue, deserialized.DefaultValue); + Assert.Equal(expectedIsValueRequired, deserialized.IsValueRequired); + Assert.Equal(expectedMaxLength, deserialized.MaxLength); + Assert.Equal(expectedMaxValue, deserialized.MaxValue); + Assert.Equal(expectedMinLength, deserialized.MinLength); + Assert.Equal(expectedMinValue, deserialized.MinValue); + Assert.NotNull(deserialized.SelectOptions); + Assert.Equal(expectedSelectOptions.Count, deserialized.SelectOptions.Count); + for (int i = 0; i < expectedSelectOptions.Count; i++) + { + Assert.Equal(expectedSelectOptions[i], deserialized.SelectOptions[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new CustomMetadataFields::Schema + { + Type = CustomMetadataFields::Type.Text, + DefaultValue = new( + [ + new CustomMetadataFields::DefaultValueItem(true), + new CustomMetadataFields::DefaultValueItem(10), + new CustomMetadataFields::DefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + SelectOptions = ["small", "medium", "large", 30, 40, true], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new CustomMetadataFields::Schema { Type = CustomMetadataFields::Type.Text }; + + Assert.Null(model.DefaultValue); + Assert.False(model.RawData.ContainsKey("defaultValue")); + Assert.Null(model.IsValueRequired); + Assert.False(model.RawData.ContainsKey("isValueRequired")); + Assert.Null(model.MaxLength); + Assert.False(model.RawData.ContainsKey("maxLength")); + Assert.Null(model.MaxValue); + Assert.False(model.RawData.ContainsKey("maxValue")); + Assert.Null(model.MinLength); + Assert.False(model.RawData.ContainsKey("minLength")); + Assert.Null(model.MinValue); + Assert.False(model.RawData.ContainsKey("minValue")); + Assert.Null(model.SelectOptions); + Assert.False(model.RawData.ContainsKey("selectOptions")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new CustomMetadataFields::Schema { Type = CustomMetadataFields::Type.Text }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new CustomMetadataFields::Schema + { + Type = CustomMetadataFields::Type.Text, + + // Null should be interpreted as omitted for these properties + DefaultValue = null, + IsValueRequired = null, + MaxLength = null, + MaxValue = null, + MinLength = null, + MinValue = null, + SelectOptions = null, + }; + + Assert.Null(model.DefaultValue); + Assert.False(model.RawData.ContainsKey("defaultValue")); + Assert.Null(model.IsValueRequired); + Assert.False(model.RawData.ContainsKey("isValueRequired")); + Assert.Null(model.MaxLength); + Assert.False(model.RawData.ContainsKey("maxLength")); + Assert.Null(model.MaxValue); + Assert.False(model.RawData.ContainsKey("maxValue")); + Assert.Null(model.MinLength); + Assert.False(model.RawData.ContainsKey("minLength")); + Assert.Null(model.MinValue); + Assert.False(model.RawData.ContainsKey("minValue")); + Assert.Null(model.SelectOptions); + Assert.False(model.RawData.ContainsKey("selectOptions")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new CustomMetadataFields::Schema + { + Type = CustomMetadataFields::Type.Text, + + // Null should be interpreted as omitted for these properties + DefaultValue = null, + IsValueRequired = null, + MaxLength = null, + MaxValue = null, + MinLength = null, + MinValue = null, + SelectOptions = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new CustomMetadataFields::Schema + { + Type = CustomMetadataFields::Type.Text, + DefaultValue = new( + [ + new CustomMetadataFields::DefaultValueItem(true), + new CustomMetadataFields::DefaultValueItem(10), + new CustomMetadataFields::DefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + SelectOptions = ["small", "medium", "large", 30, 40, true], + }; + + CustomMetadataFields::Schema copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class TypeTest : TestBase +{ + [Theory] + [InlineData(CustomMetadataFields::Type.Text)] + [InlineData(CustomMetadataFields::Type.Textarea)] + [InlineData(CustomMetadataFields::Type.Number)] + [InlineData(CustomMetadataFields::Type.Date)] + [InlineData(CustomMetadataFields::Type.Boolean)] + [InlineData(CustomMetadataFields::Type.SingleSelect)] + [InlineData(CustomMetadataFields::Type.MultiSelect)] + public void Validation_Works(CustomMetadataFields::Type rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(CustomMetadataFields::Type.Text)] + [InlineData(CustomMetadataFields::Type.Textarea)] + [InlineData(CustomMetadataFields::Type.Number)] + [InlineData(CustomMetadataFields::Type.Date)] + [InlineData(CustomMetadataFields::Type.Boolean)] + [InlineData(CustomMetadataFields::Type.SingleSelect)] + [InlineData(CustomMetadataFields::Type.MultiSelect)] + public void SerializationRoundtrip_Works(CustomMetadataFields::Type rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class DefaultValueTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + CustomMetadataFields::DefaultValue value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + CustomMetadataFields::DefaultValue value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + CustomMetadataFields::DefaultValue value = true; + value.Validate(); + } + + [Fact] + public void MixedValidationWorks() + { + CustomMetadataFields::DefaultValue value = new( + [ + new CustomMetadataFields::DefaultValueItem(true), + new CustomMetadataFields::DefaultValueItem(10), + new CustomMetadataFields::DefaultValueItem("Hello"), + ] + ); + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + CustomMetadataFields::DefaultValue value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + CustomMetadataFields::DefaultValue value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + CustomMetadataFields::DefaultValue value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void MixedSerializationRoundtripWorks() + { + CustomMetadataFields::DefaultValue value = new( + [ + new CustomMetadataFields::DefaultValueItem(true), + new CustomMetadataFields::DefaultValueItem(10), + new CustomMetadataFields::DefaultValueItem("Hello"), + ] + ); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class DefaultValueItemTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + CustomMetadataFields::DefaultValueItem value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + CustomMetadataFields::DefaultValueItem value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + CustomMetadataFields::DefaultValueItem value = true; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + CustomMetadataFields::DefaultValueItem value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + CustomMetadataFields::DefaultValueItem value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + CustomMetadataFields::DefaultValueItem value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class MaxValueTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + CustomMetadataFields::MaxValue value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + CustomMetadataFields::MaxValue value = 0; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + CustomMetadataFields::MaxValue value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + CustomMetadataFields::MaxValue value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class MinValueTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + CustomMetadataFields::MinValue value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + CustomMetadataFields::MinValue value = 0; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + CustomMetadataFields::MinValue value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + CustomMetadataFields::MinValue value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class SelectOptionTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + CustomMetadataFields::SelectOption value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + CustomMetadataFields::SelectOption value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + CustomMetadataFields::SelectOption value = true; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + CustomMetadataFields::SelectOption value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + CustomMetadataFields::SelectOption value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + CustomMetadataFields::SelectOption value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/CustomMetadataFields/CustomMetadataFieldDeleteParamsTest.cs b/src/Imagekit.Tests/Models/CustomMetadataFields/CustomMetadataFieldDeleteParamsTest.cs new file mode 100644 index 00000000..f55ac27c --- /dev/null +++ b/src/Imagekit.Tests/Models/CustomMetadataFields/CustomMetadataFieldDeleteParamsTest.cs @@ -0,0 +1,39 @@ +using System; +using Imagekit.Models.CustomMetadataFields; + +namespace Imagekit.Tests.Models.CustomMetadataFields; + +public class CustomMetadataFieldDeleteParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new CustomMetadataFieldDeleteParams { ID = "id" }; + + string expectedID = "id"; + + Assert.Equal(expectedID, parameters.ID); + } + + [Fact] + public void Url_Works() + { + CustomMetadataFieldDeleteParams parameters = new() { ID = "id" }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/customMetadataFields/id"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new CustomMetadataFieldDeleteParams { ID = "id" }; + + CustomMetadataFieldDeleteParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/CustomMetadataFields/CustomMetadataFieldDeleteResponseTest.cs b/src/Imagekit.Tests/Models/CustomMetadataFields/CustomMetadataFieldDeleteResponseTest.cs new file mode 100644 index 00000000..355807b5 --- /dev/null +++ b/src/Imagekit.Tests/Models/CustomMetadataFields/CustomMetadataFieldDeleteResponseTest.cs @@ -0,0 +1,59 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.CustomMetadataFields; + +namespace Imagekit.Tests.Models.CustomMetadataFields; + +public class CustomMetadataFieldDeleteResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new CustomMetadataFieldDeleteResponse { }; + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new CustomMetadataFieldDeleteResponse { }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new CustomMetadataFieldDeleteResponse { }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + } + + [Fact] + public void Validation_Works() + { + var model = new CustomMetadataFieldDeleteResponse { }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new CustomMetadataFieldDeleteResponse { }; + + CustomMetadataFieldDeleteResponse copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/CustomMetadataFields/CustomMetadataFieldListParamsTest.cs b/src/Imagekit.Tests/Models/CustomMetadataFields/CustomMetadataFieldListParamsTest.cs new file mode 100644 index 00000000..63cf7617 --- /dev/null +++ b/src/Imagekit.Tests/Models/CustomMetadataFields/CustomMetadataFieldListParamsTest.cs @@ -0,0 +1,85 @@ +using System; +using Imagekit.Models.CustomMetadataFields; + +namespace Imagekit.Tests.Models.CustomMetadataFields; + +public class CustomMetadataFieldListParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new CustomMetadataFieldListParams + { + FolderPath = "folderPath", + IncludeDeleted = true, + }; + + string expectedFolderPath = "folderPath"; + bool expectedIncludeDeleted = true; + + Assert.Equal(expectedFolderPath, parameters.FolderPath); + Assert.Equal(expectedIncludeDeleted, parameters.IncludeDeleted); + } + + [Fact] + public void OptionalNonNullableParamsUnsetAreNotSet_Works() + { + var parameters = new CustomMetadataFieldListParams { }; + + Assert.Null(parameters.FolderPath); + Assert.False(parameters.RawQueryData.ContainsKey("folderPath")); + Assert.Null(parameters.IncludeDeleted); + Assert.False(parameters.RawQueryData.ContainsKey("includeDeleted")); + } + + [Fact] + public void OptionalNonNullableParamsSetToNullAreNotSet_Works() + { + var parameters = new CustomMetadataFieldListParams + { + // Null should be interpreted as omitted for these properties + FolderPath = null, + IncludeDeleted = null, + }; + + Assert.Null(parameters.FolderPath); + Assert.False(parameters.RawQueryData.ContainsKey("folderPath")); + Assert.Null(parameters.IncludeDeleted); + Assert.False(parameters.RawQueryData.ContainsKey("includeDeleted")); + } + + [Fact] + public void Url_Works() + { + CustomMetadataFieldListParams parameters = new() + { + FolderPath = "folderPath", + IncludeDeleted = true, + }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual( + new Uri( + "https://api.imagekit.io/v1/customMetadataFields?folderPath=folderPath&includeDeleted=true" + ), + url + ) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new CustomMetadataFieldListParams + { + FolderPath = "folderPath", + IncludeDeleted = true, + }; + + CustomMetadataFieldListParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/CustomMetadataFields/CustomMetadataFieldTest.cs b/src/Imagekit.Tests/Models/CustomMetadataFields/CustomMetadataFieldTest.cs new file mode 100644 index 00000000..adc8d707 --- /dev/null +++ b/src/Imagekit.Tests/Models/CustomMetadataFields/CustomMetadataFieldTest.cs @@ -0,0 +1,891 @@ +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.CustomMetadataFields; + +namespace Imagekit.Tests.Models.CustomMetadataFields; + +public class CustomMetadataFieldTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new CustomMetadataField + { + ID = "id", + Label = "label", + Name = "name", + Schema = new() + { + Type = CustomMetadataFieldSchemaType.Text, + DefaultValue = new( + [ + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + SelectOptions = ["small", "medium", "large", 30, 40, true], + }, + }; + + string expectedID = "id"; + string expectedLabel = "label"; + string expectedName = "name"; + CustomMetadataFieldSchema expectedSchema = new() + { + Type = CustomMetadataFieldSchemaType.Text, + DefaultValue = new( + [ + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + SelectOptions = ["small", "medium", "large", 30, 40, true], + }; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedLabel, model.Label); + Assert.Equal(expectedName, model.Name); + Assert.Equal(expectedSchema, model.Schema); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new CustomMetadataField + { + ID = "id", + Label = "label", + Name = "name", + Schema = new() + { + Type = CustomMetadataFieldSchemaType.Text, + DefaultValue = new( + [ + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + SelectOptions = ["small", "medium", "large", 30, 40, true], + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new CustomMetadataField + { + ID = "id", + Label = "label", + Name = "name", + Schema = new() + { + Type = CustomMetadataFieldSchemaType.Text, + DefaultValue = new( + [ + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + SelectOptions = ["small", "medium", "large", 30, 40, true], + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedLabel = "label"; + string expectedName = "name"; + CustomMetadataFieldSchema expectedSchema = new() + { + Type = CustomMetadataFieldSchemaType.Text, + DefaultValue = new( + [ + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + SelectOptions = ["small", "medium", "large", 30, 40, true], + }; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedLabel, deserialized.Label); + Assert.Equal(expectedName, deserialized.Name); + Assert.Equal(expectedSchema, deserialized.Schema); + } + + [Fact] + public void Validation_Works() + { + var model = new CustomMetadataField + { + ID = "id", + Label = "label", + Name = "name", + Schema = new() + { + Type = CustomMetadataFieldSchemaType.Text, + DefaultValue = new( + [ + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + SelectOptions = ["small", "medium", "large", 30, 40, true], + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new CustomMetadataField + { + ID = "id", + Label = "label", + Name = "name", + Schema = new() + { + Type = CustomMetadataFieldSchemaType.Text, + DefaultValue = new( + [ + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + SelectOptions = ["small", "medium", "large", 30, 40, true], + }, + }; + + CustomMetadataField copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class CustomMetadataFieldSchemaTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new CustomMetadataFieldSchema + { + Type = CustomMetadataFieldSchemaType.Text, + DefaultValue = new( + [ + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + SelectOptions = ["small", "medium", "large", 30, 40, true], + }; + + ApiEnum expectedType = + CustomMetadataFieldSchemaType.Text; + CustomMetadataFieldSchemaDefaultValue expectedDefaultValue = new( + [ + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem("Hello"), + ] + ); + bool expectedIsValueRequired = true; + double expectedMaxLength = 0; + CustomMetadataFieldSchemaMaxValue expectedMaxValue = "string"; + double expectedMinLength = 0; + CustomMetadataFieldSchemaMinValue expectedMinValue = "string"; + List expectedSelectOptions = + [ + "small", + "medium", + "large", + 30, + 40, + true, + ]; + + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedDefaultValue, model.DefaultValue); + Assert.Equal(expectedIsValueRequired, model.IsValueRequired); + Assert.Equal(expectedMaxLength, model.MaxLength); + Assert.Equal(expectedMaxValue, model.MaxValue); + Assert.Equal(expectedMinLength, model.MinLength); + Assert.Equal(expectedMinValue, model.MinValue); + Assert.NotNull(model.SelectOptions); + Assert.Equal(expectedSelectOptions.Count, model.SelectOptions.Count); + for (int i = 0; i < expectedSelectOptions.Count; i++) + { + Assert.Equal(expectedSelectOptions[i], model.SelectOptions[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new CustomMetadataFieldSchema + { + Type = CustomMetadataFieldSchemaType.Text, + DefaultValue = new( + [ + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + SelectOptions = ["small", "medium", "large", 30, 40, true], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new CustomMetadataFieldSchema + { + Type = CustomMetadataFieldSchemaType.Text, + DefaultValue = new( + [ + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + SelectOptions = ["small", "medium", "large", 30, 40, true], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum expectedType = + CustomMetadataFieldSchemaType.Text; + CustomMetadataFieldSchemaDefaultValue expectedDefaultValue = new( + [ + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem("Hello"), + ] + ); + bool expectedIsValueRequired = true; + double expectedMaxLength = 0; + CustomMetadataFieldSchemaMaxValue expectedMaxValue = "string"; + double expectedMinLength = 0; + CustomMetadataFieldSchemaMinValue expectedMinValue = "string"; + List expectedSelectOptions = + [ + "small", + "medium", + "large", + 30, + 40, + true, + ]; + + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedDefaultValue, deserialized.DefaultValue); + Assert.Equal(expectedIsValueRequired, deserialized.IsValueRequired); + Assert.Equal(expectedMaxLength, deserialized.MaxLength); + Assert.Equal(expectedMaxValue, deserialized.MaxValue); + Assert.Equal(expectedMinLength, deserialized.MinLength); + Assert.Equal(expectedMinValue, deserialized.MinValue); + Assert.NotNull(deserialized.SelectOptions); + Assert.Equal(expectedSelectOptions.Count, deserialized.SelectOptions.Count); + for (int i = 0; i < expectedSelectOptions.Count; i++) + { + Assert.Equal(expectedSelectOptions[i], deserialized.SelectOptions[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new CustomMetadataFieldSchema + { + Type = CustomMetadataFieldSchemaType.Text, + DefaultValue = new( + [ + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + SelectOptions = ["small", "medium", "large", 30, 40, true], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new CustomMetadataFieldSchema { Type = CustomMetadataFieldSchemaType.Text }; + + Assert.Null(model.DefaultValue); + Assert.False(model.RawData.ContainsKey("defaultValue")); + Assert.Null(model.IsValueRequired); + Assert.False(model.RawData.ContainsKey("isValueRequired")); + Assert.Null(model.MaxLength); + Assert.False(model.RawData.ContainsKey("maxLength")); + Assert.Null(model.MaxValue); + Assert.False(model.RawData.ContainsKey("maxValue")); + Assert.Null(model.MinLength); + Assert.False(model.RawData.ContainsKey("minLength")); + Assert.Null(model.MinValue); + Assert.False(model.RawData.ContainsKey("minValue")); + Assert.Null(model.SelectOptions); + Assert.False(model.RawData.ContainsKey("selectOptions")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new CustomMetadataFieldSchema { Type = CustomMetadataFieldSchemaType.Text }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new CustomMetadataFieldSchema + { + Type = CustomMetadataFieldSchemaType.Text, + + // Null should be interpreted as omitted for these properties + DefaultValue = null, + IsValueRequired = null, + MaxLength = null, + MaxValue = null, + MinLength = null, + MinValue = null, + SelectOptions = null, + }; + + Assert.Null(model.DefaultValue); + Assert.False(model.RawData.ContainsKey("defaultValue")); + Assert.Null(model.IsValueRequired); + Assert.False(model.RawData.ContainsKey("isValueRequired")); + Assert.Null(model.MaxLength); + Assert.False(model.RawData.ContainsKey("maxLength")); + Assert.Null(model.MaxValue); + Assert.False(model.RawData.ContainsKey("maxValue")); + Assert.Null(model.MinLength); + Assert.False(model.RawData.ContainsKey("minLength")); + Assert.Null(model.MinValue); + Assert.False(model.RawData.ContainsKey("minValue")); + Assert.Null(model.SelectOptions); + Assert.False(model.RawData.ContainsKey("selectOptions")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new CustomMetadataFieldSchema + { + Type = CustomMetadataFieldSchemaType.Text, + + // Null should be interpreted as omitted for these properties + DefaultValue = null, + IsValueRequired = null, + MaxLength = null, + MaxValue = null, + MinLength = null, + MinValue = null, + SelectOptions = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new CustomMetadataFieldSchema + { + Type = CustomMetadataFieldSchemaType.Text, + DefaultValue = new( + [ + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + SelectOptions = ["small", "medium", "large", 30, 40, true], + }; + + CustomMetadataFieldSchema copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class CustomMetadataFieldSchemaTypeTest : TestBase +{ + [Theory] + [InlineData(CustomMetadataFieldSchemaType.Text)] + [InlineData(CustomMetadataFieldSchemaType.Textarea)] + [InlineData(CustomMetadataFieldSchemaType.Number)] + [InlineData(CustomMetadataFieldSchemaType.Date)] + [InlineData(CustomMetadataFieldSchemaType.Boolean)] + [InlineData(CustomMetadataFieldSchemaType.SingleSelect)] + [InlineData(CustomMetadataFieldSchemaType.MultiSelect)] + public void Validation_Works(CustomMetadataFieldSchemaType rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(CustomMetadataFieldSchemaType.Text)] + [InlineData(CustomMetadataFieldSchemaType.Textarea)] + [InlineData(CustomMetadataFieldSchemaType.Number)] + [InlineData(CustomMetadataFieldSchemaType.Date)] + [InlineData(CustomMetadataFieldSchemaType.Boolean)] + [InlineData(CustomMetadataFieldSchemaType.SingleSelect)] + [InlineData(CustomMetadataFieldSchemaType.MultiSelect)] + public void SerializationRoundtrip_Works(CustomMetadataFieldSchemaType rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class CustomMetadataFieldSchemaDefaultValueTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + CustomMetadataFieldSchemaDefaultValue value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + CustomMetadataFieldSchemaDefaultValue value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + CustomMetadataFieldSchemaDefaultValue value = true; + value.Validate(); + } + + [Fact] + public void MixedValidationWorks() + { + CustomMetadataFieldSchemaDefaultValue value = new( + [ + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem("Hello"), + ] + ); + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + CustomMetadataFieldSchemaDefaultValue value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + CustomMetadataFieldSchemaDefaultValue value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + CustomMetadataFieldSchemaDefaultValue value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void MixedSerializationRoundtripWorks() + { + CustomMetadataFieldSchemaDefaultValue value = new( + [ + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldSchemaDefaultValueDefaultValueItem("Hello"), + ] + ); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class CustomMetadataFieldSchemaDefaultValueDefaultValueItemTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + CustomMetadataFieldSchemaDefaultValueDefaultValueItem value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + CustomMetadataFieldSchemaDefaultValueDefaultValueItem value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + CustomMetadataFieldSchemaDefaultValueDefaultValueItem value = true; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + CustomMetadataFieldSchemaDefaultValueDefaultValueItem value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + CustomMetadataFieldSchemaDefaultValueDefaultValueItem value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + CustomMetadataFieldSchemaDefaultValueDefaultValueItem value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class CustomMetadataFieldSchemaMaxValueTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + CustomMetadataFieldSchemaMaxValue value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + CustomMetadataFieldSchemaMaxValue value = 0; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + CustomMetadataFieldSchemaMaxValue value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + CustomMetadataFieldSchemaMaxValue value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class CustomMetadataFieldSchemaMinValueTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + CustomMetadataFieldSchemaMinValue value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + CustomMetadataFieldSchemaMinValue value = 0; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + CustomMetadataFieldSchemaMinValue value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + CustomMetadataFieldSchemaMinValue value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class CustomMetadataFieldSchemaSelectOptionTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + CustomMetadataFieldSchemaSelectOption value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + CustomMetadataFieldSchemaSelectOption value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + CustomMetadataFieldSchemaSelectOption value = true; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + CustomMetadataFieldSchemaSelectOption value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + CustomMetadataFieldSchemaSelectOption value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + CustomMetadataFieldSchemaSelectOption value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/CustomMetadataFields/CustomMetadataFieldUpdateParamsTest.cs b/src/Imagekit.Tests/Models/CustomMetadataFields/CustomMetadataFieldUpdateParamsTest.cs new file mode 100644 index 00000000..26dbc490 --- /dev/null +++ b/src/Imagekit.Tests/Models/CustomMetadataFields/CustomMetadataFieldUpdateParamsTest.cs @@ -0,0 +1,732 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.CustomMetadataFields; + +namespace Imagekit.Tests.Models.CustomMetadataFields; + +public class CustomMetadataFieldUpdateParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new CustomMetadataFieldUpdateParams + { + ID = "id", + Label = "price", + Schema = new() + { + DefaultValue = new( + [ + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem( + "Hello" + ), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = 3000, + MinLength = 0, + MinValue = 1000, + SelectOptions = ["small", "medium", "large", 30, 40, true], + }, + }; + + string expectedID = "id"; + string expectedLabel = "price"; + CustomMetadataFieldUpdateParamsSchema expectedSchema = new() + { + DefaultValue = new( + [ + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = 3000, + MinLength = 0, + MinValue = 1000, + SelectOptions = ["small", "medium", "large", 30, 40, true], + }; + + Assert.Equal(expectedID, parameters.ID); + Assert.Equal(expectedLabel, parameters.Label); + Assert.Equal(expectedSchema, parameters.Schema); + } + + [Fact] + public void OptionalNonNullableParamsUnsetAreNotSet_Works() + { + var parameters = new CustomMetadataFieldUpdateParams { ID = "id" }; + + Assert.Null(parameters.Label); + Assert.False(parameters.RawBodyData.ContainsKey("label")); + Assert.Null(parameters.Schema); + Assert.False(parameters.RawBodyData.ContainsKey("schema")); + } + + [Fact] + public void OptionalNonNullableParamsSetToNullAreNotSet_Works() + { + var parameters = new CustomMetadataFieldUpdateParams + { + ID = "id", + + // Null should be interpreted as omitted for these properties + Label = null, + Schema = null, + }; + + Assert.Null(parameters.Label); + Assert.False(parameters.RawBodyData.ContainsKey("label")); + Assert.Null(parameters.Schema); + Assert.False(parameters.RawBodyData.ContainsKey("schema")); + } + + [Fact] + public void Url_Works() + { + CustomMetadataFieldUpdateParams parameters = new() { ID = "id" }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/customMetadataFields/id"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new CustomMetadataFieldUpdateParams + { + ID = "id", + Label = "price", + Schema = new() + { + DefaultValue = new( + [ + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem( + "Hello" + ), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = 3000, + MinLength = 0, + MinValue = 1000, + SelectOptions = ["small", "medium", "large", 30, 40, true], + }, + }; + + CustomMetadataFieldUpdateParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} + +public class CustomMetadataFieldUpdateParamsSchemaTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new CustomMetadataFieldUpdateParamsSchema + { + DefaultValue = new( + [ + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + SelectOptions = ["small", "medium", "large", 30, 40, true], + }; + + CustomMetadataFieldUpdateParamsSchemaDefaultValue expectedDefaultValue = new( + [ + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem("Hello"), + ] + ); + bool expectedIsValueRequired = true; + double expectedMaxLength = 0; + CustomMetadataFieldUpdateParamsSchemaMaxValue expectedMaxValue = "string"; + double expectedMinLength = 0; + CustomMetadataFieldUpdateParamsSchemaMinValue expectedMinValue = "string"; + List expectedSelectOptions = + [ + "small", + "medium", + "large", + 30, + 40, + true, + ]; + + Assert.Equal(expectedDefaultValue, model.DefaultValue); + Assert.Equal(expectedIsValueRequired, model.IsValueRequired); + Assert.Equal(expectedMaxLength, model.MaxLength); + Assert.Equal(expectedMaxValue, model.MaxValue); + Assert.Equal(expectedMinLength, model.MinLength); + Assert.Equal(expectedMinValue, model.MinValue); + Assert.NotNull(model.SelectOptions); + Assert.Equal(expectedSelectOptions.Count, model.SelectOptions.Count); + for (int i = 0; i < expectedSelectOptions.Count; i++) + { + Assert.Equal(expectedSelectOptions[i], model.SelectOptions[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new CustomMetadataFieldUpdateParamsSchema + { + DefaultValue = new( + [ + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + SelectOptions = ["small", "medium", "large", 30, 40, true], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new CustomMetadataFieldUpdateParamsSchema + { + DefaultValue = new( + [ + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + SelectOptions = ["small", "medium", "large", 30, 40, true], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + CustomMetadataFieldUpdateParamsSchemaDefaultValue expectedDefaultValue = new( + [ + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem("Hello"), + ] + ); + bool expectedIsValueRequired = true; + double expectedMaxLength = 0; + CustomMetadataFieldUpdateParamsSchemaMaxValue expectedMaxValue = "string"; + double expectedMinLength = 0; + CustomMetadataFieldUpdateParamsSchemaMinValue expectedMinValue = "string"; + List expectedSelectOptions = + [ + "small", + "medium", + "large", + 30, + 40, + true, + ]; + + Assert.Equal(expectedDefaultValue, deserialized.DefaultValue); + Assert.Equal(expectedIsValueRequired, deserialized.IsValueRequired); + Assert.Equal(expectedMaxLength, deserialized.MaxLength); + Assert.Equal(expectedMaxValue, deserialized.MaxValue); + Assert.Equal(expectedMinLength, deserialized.MinLength); + Assert.Equal(expectedMinValue, deserialized.MinValue); + Assert.NotNull(deserialized.SelectOptions); + Assert.Equal(expectedSelectOptions.Count, deserialized.SelectOptions.Count); + for (int i = 0; i < expectedSelectOptions.Count; i++) + { + Assert.Equal(expectedSelectOptions[i], deserialized.SelectOptions[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new CustomMetadataFieldUpdateParamsSchema + { + DefaultValue = new( + [ + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + SelectOptions = ["small", "medium", "large", 30, 40, true], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new CustomMetadataFieldUpdateParamsSchema { }; + + Assert.Null(model.DefaultValue); + Assert.False(model.RawData.ContainsKey("defaultValue")); + Assert.Null(model.IsValueRequired); + Assert.False(model.RawData.ContainsKey("isValueRequired")); + Assert.Null(model.MaxLength); + Assert.False(model.RawData.ContainsKey("maxLength")); + Assert.Null(model.MaxValue); + Assert.False(model.RawData.ContainsKey("maxValue")); + Assert.Null(model.MinLength); + Assert.False(model.RawData.ContainsKey("minLength")); + Assert.Null(model.MinValue); + Assert.False(model.RawData.ContainsKey("minValue")); + Assert.Null(model.SelectOptions); + Assert.False(model.RawData.ContainsKey("selectOptions")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new CustomMetadataFieldUpdateParamsSchema { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new CustomMetadataFieldUpdateParamsSchema + { + // Null should be interpreted as omitted for these properties + DefaultValue = null, + IsValueRequired = null, + MaxLength = null, + MaxValue = null, + MinLength = null, + MinValue = null, + SelectOptions = null, + }; + + Assert.Null(model.DefaultValue); + Assert.False(model.RawData.ContainsKey("defaultValue")); + Assert.Null(model.IsValueRequired); + Assert.False(model.RawData.ContainsKey("isValueRequired")); + Assert.Null(model.MaxLength); + Assert.False(model.RawData.ContainsKey("maxLength")); + Assert.Null(model.MaxValue); + Assert.False(model.RawData.ContainsKey("maxValue")); + Assert.Null(model.MinLength); + Assert.False(model.RawData.ContainsKey("minLength")); + Assert.Null(model.MinValue); + Assert.False(model.RawData.ContainsKey("minValue")); + Assert.Null(model.SelectOptions); + Assert.False(model.RawData.ContainsKey("selectOptions")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new CustomMetadataFieldUpdateParamsSchema + { + // Null should be interpreted as omitted for these properties + DefaultValue = null, + IsValueRequired = null, + MaxLength = null, + MaxValue = null, + MinLength = null, + MinValue = null, + SelectOptions = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new CustomMetadataFieldUpdateParamsSchema + { + DefaultValue = new( + [ + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + SelectOptions = ["small", "medium", "large", 30, 40, true], + }; + + CustomMetadataFieldUpdateParamsSchema copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class CustomMetadataFieldUpdateParamsSchemaDefaultValueTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + CustomMetadataFieldUpdateParamsSchemaDefaultValue value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + CustomMetadataFieldUpdateParamsSchemaDefaultValue value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + CustomMetadataFieldUpdateParamsSchemaDefaultValue value = true; + value.Validate(); + } + + [Fact] + public void MixedValidationWorks() + { + CustomMetadataFieldUpdateParamsSchemaDefaultValue value = new( + [ + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem("Hello"), + ] + ); + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + CustomMetadataFieldUpdateParamsSchemaDefaultValue value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + CustomMetadataFieldUpdateParamsSchemaDefaultValue value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + CustomMetadataFieldUpdateParamsSchemaDefaultValue value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void MixedSerializationRoundtripWorks() + { + CustomMetadataFieldUpdateParamsSchemaDefaultValue value = new( + [ + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(true), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(10), + new CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem("Hello"), + ] + ); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItemTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem value = true; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class CustomMetadataFieldUpdateParamsSchemaMaxValueTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + CustomMetadataFieldUpdateParamsSchemaMaxValue value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + CustomMetadataFieldUpdateParamsSchemaMaxValue value = 0; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + CustomMetadataFieldUpdateParamsSchemaMaxValue value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + CustomMetadataFieldUpdateParamsSchemaMaxValue value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class CustomMetadataFieldUpdateParamsSchemaMinValueTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + CustomMetadataFieldUpdateParamsSchemaMinValue value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + CustomMetadataFieldUpdateParamsSchemaMinValue value = 0; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + CustomMetadataFieldUpdateParamsSchemaMinValue value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + CustomMetadataFieldUpdateParamsSchemaMinValue value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class CustomMetadataFieldUpdateParamsSchemaSelectOptionTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + CustomMetadataFieldUpdateParamsSchemaSelectOption value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + CustomMetadataFieldUpdateParamsSchemaSelectOption value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + CustomMetadataFieldUpdateParamsSchemaSelectOption value = true; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + CustomMetadataFieldUpdateParamsSchemaSelectOption value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + CustomMetadataFieldUpdateParamsSchemaSelectOption value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + CustomMetadataFieldUpdateParamsSchemaSelectOption value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/Dummy/DummyCreateParamsTest.cs b/src/Imagekit.Tests/Models/Dummy/DummyCreateParamsTest.cs new file mode 100644 index 00000000..ee2f576b --- /dev/null +++ b/src/Imagekit.Tests/Models/Dummy/DummyCreateParamsTest.cs @@ -0,0 +1,2810 @@ +using System; +using System.Collections.Generic; +using Imagekit.Core; +using Imagekit.Models; +using Imagekit.Models.Dummy; + +namespace Imagekit.Tests.Models.Dummy; + +public class DummyCreateParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new DummyCreateParams + { + BaseOverlay = new() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + }, + ExtensionConfig = new RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + Extensions = + [ + new ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 5, + MinConfidence = 95, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new ExtensionItemAIAutoDescription(), + new ExtensionItemAITasks( + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + new ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }, + ] + ), + new SavedExtension("ext_abc123"), + ], + GetImageAttributesOptions = new() + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + ExpiresIn = 0, + QueryParameters = new Dictionary() { { "foo", "string" } }, + Signed = true, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + TransformationPosition = TransformationPosition.Path, + DeviceBreakpoints = [640, 750, 828, 1080, 1200, 1920, 2048, 3840], + ImageBreakpoints = [16, 32, 48, 64, 96, 128, 256, 384], + Sizes = "(min-width: 768px) 50vw, 100vw", + Width = 400, + }, + ImageOverlay = new() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = Encoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + OverlayPosition = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + OverlayTiming = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + ResponsiveImageAttributes = new() + { + Src = "https://ik.imagekit.io/demo/image.jpg?tr=w-3840", + Sizes = "100vw", + SrcSet = + "https://ik.imagekit.io/demo/image.jpg?tr=w-640 640w, https://ik.imagekit.io/demo/image.jpg?tr=w-1080 1080w, https://ik.imagekit.io/demo/image.jpg?tr=w-1920 1920w", + Width = 400, + }, + SavedExtensions = new() + { + ID = "ext_abc123", + Config = new RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Description = "Analyzes vehicle images for type, condition, and quality assessment", + Name = "Car Quality Analysis", + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + }, + SolidColorOverlay = new() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Color = "color", + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }, + ], + }, + SolidColorOverlayTransformation = new() + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }, + SrcOptions = new() + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + ExpiresIn = 0, + QueryParameters = new Dictionary() { { "foo", "string" } }, + Signed = true, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + TransformationPosition = TransformationPosition.Path, + }, + StreamingResolution = StreamingResolution.V240, + SubtitleOverlay = new() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = SubtitleOverlaySubtitleOverlayEncoding.Auto, + Transformation = + [ + new() + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }, + ], + }, + SubtitleOverlayTransformation = new() + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }, + TextOverlay = new() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + TextOverlayTransformation = new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + Transformation = new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + TransformationPosition = TransformationPosition.Path, + VideoOverlay = new() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = VideoOverlayVideoOverlayEncoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }, + }; + + BaseOverlay expectedBaseOverlay = new() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + }; + ExtensionConfig expectedExtensionConfig = new RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }; + List expectedExtensions = + [ + new ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 5, + MinConfidence = 95, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new ExtensionItemAIAutoDescription(), + new ExtensionItemAITasks( + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + new ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }, + ] + ), + new SavedExtension("ext_abc123"), + ]; + GetImageAttributesOptions expectedGetImageAttributesOptions = new() + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + ExpiresIn = 0, + QueryParameters = new Dictionary() { { "foo", "string" } }, + Signed = true, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + TransformationPosition = TransformationPosition.Path, + DeviceBreakpoints = [640, 750, 828, 1080, 1200, 1920, 2048, 3840], + ImageBreakpoints = [16, 32, 48, 64, 96, 128, 256, 384], + Sizes = "(min-width: 768px) 50vw, 100vw", + Width = 400, + }; + ImageOverlay expectedImageOverlay = new() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = Encoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + Overlay expectedOverlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }; + OverlayPosition expectedOverlayPosition = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }; + OverlayTiming expectedOverlayTiming = new() + { + Duration = 0, + End = 0, + Start = 0, + }; + ResponsiveImageAttributes expectedResponsiveImageAttributes = new() + { + Src = "https://ik.imagekit.io/demo/image.jpg?tr=w-3840", + Sizes = "100vw", + SrcSet = + "https://ik.imagekit.io/demo/image.jpg?tr=w-640 640w, https://ik.imagekit.io/demo/image.jpg?tr=w-1080 1080w, https://ik.imagekit.io/demo/image.jpg?tr=w-1920 1920w", + Width = 400, + }; + SharedSavedExtension expectedSavedExtensions = new() + { + ID = "ext_abc123", + Config = new RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Description = "Analyzes vehicle images for type, condition, and quality assessment", + Name = "Car Quality Analysis", + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + }; + SolidColorOverlay expectedSolidColorOverlay = new() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Color = "color", + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }, + ], + }; + SolidColorOverlayTransformation expectedSolidColorOverlayTransformation = new() + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }; + SrcOptions expectedSrcOptions = new() + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + ExpiresIn = 0, + QueryParameters = new Dictionary() { { "foo", "string" } }, + Signed = true, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + TransformationPosition = TransformationPosition.Path, + }; + ApiEnum expectedStreamingResolution = StreamingResolution.V240; + SubtitleOverlay expectedSubtitleOverlay = new() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = SubtitleOverlaySubtitleOverlayEncoding.Auto, + Transformation = + [ + new() + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }, + ], + }; + SubtitleOverlayTransformation expectedSubtitleOverlayTransformation = new() + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }; + TextOverlay expectedTextOverlay = new() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }; + TextOverlayTransformation expectedTextOverlayTransformation = new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }; + Transformation expectedTransformation = new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }; + ApiEnum expectedTransformationPosition = + TransformationPosition.Path; + VideoOverlay expectedVideoOverlay = new() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = VideoOverlayVideoOverlayEncoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + + Assert.Equal(expectedBaseOverlay, parameters.BaseOverlay); + Assert.Equal(expectedExtensionConfig, parameters.ExtensionConfig); + Assert.NotNull(parameters.Extensions); + Assert.Equal(expectedExtensions.Count, parameters.Extensions.Count); + for (int i = 0; i < expectedExtensions.Count; i++) + { + Assert.Equal(expectedExtensions[i], parameters.Extensions[i]); + } + Assert.Equal(expectedGetImageAttributesOptions, parameters.GetImageAttributesOptions); + Assert.Equal(expectedImageOverlay, parameters.ImageOverlay); + Assert.Equal(expectedOverlay, parameters.Overlay); + Assert.Equal(expectedOverlayPosition, parameters.OverlayPosition); + Assert.Equal(expectedOverlayTiming, parameters.OverlayTiming); + Assert.Equal(expectedResponsiveImageAttributes, parameters.ResponsiveImageAttributes); + Assert.Equal(expectedSavedExtensions, parameters.SavedExtensions); + Assert.Equal(expectedSolidColorOverlay, parameters.SolidColorOverlay); + Assert.Equal( + expectedSolidColorOverlayTransformation, + parameters.SolidColorOverlayTransformation + ); + Assert.Equal(expectedSrcOptions, parameters.SrcOptions); + Assert.Equal(expectedStreamingResolution, parameters.StreamingResolution); + Assert.Equal(expectedSubtitleOverlay, parameters.SubtitleOverlay); + Assert.Equal( + expectedSubtitleOverlayTransformation, + parameters.SubtitleOverlayTransformation + ); + Assert.Equal(expectedTextOverlay, parameters.TextOverlay); + Assert.Equal(expectedTextOverlayTransformation, parameters.TextOverlayTransformation); + Assert.Equal(expectedTransformation, parameters.Transformation); + Assert.Equal(expectedTransformationPosition, parameters.TransformationPosition); + Assert.Equal(expectedVideoOverlay, parameters.VideoOverlay); + } + + [Fact] + public void OptionalNonNullableParamsUnsetAreNotSet_Works() + { + var parameters = new DummyCreateParams { }; + + Assert.Null(parameters.BaseOverlay); + Assert.False(parameters.RawBodyData.ContainsKey("baseOverlay")); + Assert.Null(parameters.ExtensionConfig); + Assert.False(parameters.RawBodyData.ContainsKey("extensionConfig")); + Assert.Null(parameters.Extensions); + Assert.False(parameters.RawBodyData.ContainsKey("extensions")); + Assert.Null(parameters.GetImageAttributesOptions); + Assert.False(parameters.RawBodyData.ContainsKey("getImageAttributesOptions")); + Assert.Null(parameters.ImageOverlay); + Assert.False(parameters.RawBodyData.ContainsKey("imageOverlay")); + Assert.Null(parameters.Overlay); + Assert.False(parameters.RawBodyData.ContainsKey("overlay")); + Assert.Null(parameters.OverlayPosition); + Assert.False(parameters.RawBodyData.ContainsKey("overlayPosition")); + Assert.Null(parameters.OverlayTiming); + Assert.False(parameters.RawBodyData.ContainsKey("overlayTiming")); + Assert.Null(parameters.ResponsiveImageAttributes); + Assert.False(parameters.RawBodyData.ContainsKey("responsiveImageAttributes")); + Assert.Null(parameters.SavedExtensions); + Assert.False(parameters.RawBodyData.ContainsKey("savedExtensions")); + Assert.Null(parameters.SolidColorOverlay); + Assert.False(parameters.RawBodyData.ContainsKey("solidColorOverlay")); + Assert.Null(parameters.SolidColorOverlayTransformation); + Assert.False(parameters.RawBodyData.ContainsKey("solidColorOverlayTransformation")); + Assert.Null(parameters.SrcOptions); + Assert.False(parameters.RawBodyData.ContainsKey("srcOptions")); + Assert.Null(parameters.StreamingResolution); + Assert.False(parameters.RawBodyData.ContainsKey("streamingResolution")); + Assert.Null(parameters.SubtitleOverlay); + Assert.False(parameters.RawBodyData.ContainsKey("subtitleOverlay")); + Assert.Null(parameters.SubtitleOverlayTransformation); + Assert.False(parameters.RawBodyData.ContainsKey("subtitleOverlayTransformation")); + Assert.Null(parameters.TextOverlay); + Assert.False(parameters.RawBodyData.ContainsKey("textOverlay")); + Assert.Null(parameters.TextOverlayTransformation); + Assert.False(parameters.RawBodyData.ContainsKey("textOverlayTransformation")); + Assert.Null(parameters.Transformation); + Assert.False(parameters.RawBodyData.ContainsKey("transformation")); + Assert.Null(parameters.TransformationPosition); + Assert.False(parameters.RawBodyData.ContainsKey("transformationPosition")); + Assert.Null(parameters.VideoOverlay); + Assert.False(parameters.RawBodyData.ContainsKey("videoOverlay")); + } + + [Fact] + public void OptionalNonNullableParamsSetToNullAreNotSet_Works() + { + var parameters = new DummyCreateParams + { + // Null should be interpreted as omitted for these properties + BaseOverlay = null, + ExtensionConfig = null, + Extensions = null, + GetImageAttributesOptions = null, + ImageOverlay = null, + Overlay = null, + OverlayPosition = null, + OverlayTiming = null, + ResponsiveImageAttributes = null, + SavedExtensions = null, + SolidColorOverlay = null, + SolidColorOverlayTransformation = null, + SrcOptions = null, + StreamingResolution = null, + SubtitleOverlay = null, + SubtitleOverlayTransformation = null, + TextOverlay = null, + TextOverlayTransformation = null, + Transformation = null, + TransformationPosition = null, + VideoOverlay = null, + }; + + Assert.Null(parameters.BaseOverlay); + Assert.False(parameters.RawBodyData.ContainsKey("baseOverlay")); + Assert.Null(parameters.ExtensionConfig); + Assert.False(parameters.RawBodyData.ContainsKey("extensionConfig")); + Assert.Null(parameters.Extensions); + Assert.False(parameters.RawBodyData.ContainsKey("extensions")); + Assert.Null(parameters.GetImageAttributesOptions); + Assert.False(parameters.RawBodyData.ContainsKey("getImageAttributesOptions")); + Assert.Null(parameters.ImageOverlay); + Assert.False(parameters.RawBodyData.ContainsKey("imageOverlay")); + Assert.Null(parameters.Overlay); + Assert.False(parameters.RawBodyData.ContainsKey("overlay")); + Assert.Null(parameters.OverlayPosition); + Assert.False(parameters.RawBodyData.ContainsKey("overlayPosition")); + Assert.Null(parameters.OverlayTiming); + Assert.False(parameters.RawBodyData.ContainsKey("overlayTiming")); + Assert.Null(parameters.ResponsiveImageAttributes); + Assert.False(parameters.RawBodyData.ContainsKey("responsiveImageAttributes")); + Assert.Null(parameters.SavedExtensions); + Assert.False(parameters.RawBodyData.ContainsKey("savedExtensions")); + Assert.Null(parameters.SolidColorOverlay); + Assert.False(parameters.RawBodyData.ContainsKey("solidColorOverlay")); + Assert.Null(parameters.SolidColorOverlayTransformation); + Assert.False(parameters.RawBodyData.ContainsKey("solidColorOverlayTransformation")); + Assert.Null(parameters.SrcOptions); + Assert.False(parameters.RawBodyData.ContainsKey("srcOptions")); + Assert.Null(parameters.StreamingResolution); + Assert.False(parameters.RawBodyData.ContainsKey("streamingResolution")); + Assert.Null(parameters.SubtitleOverlay); + Assert.False(parameters.RawBodyData.ContainsKey("subtitleOverlay")); + Assert.Null(parameters.SubtitleOverlayTransformation); + Assert.False(parameters.RawBodyData.ContainsKey("subtitleOverlayTransformation")); + Assert.Null(parameters.TextOverlay); + Assert.False(parameters.RawBodyData.ContainsKey("textOverlay")); + Assert.Null(parameters.TextOverlayTransformation); + Assert.False(parameters.RawBodyData.ContainsKey("textOverlayTransformation")); + Assert.Null(parameters.Transformation); + Assert.False(parameters.RawBodyData.ContainsKey("transformation")); + Assert.Null(parameters.TransformationPosition); + Assert.False(parameters.RawBodyData.ContainsKey("transformationPosition")); + Assert.Null(parameters.VideoOverlay); + Assert.False(parameters.RawBodyData.ContainsKey("videoOverlay")); + } + + [Fact] + public void Url_Works() + { + DummyCreateParams parameters = new(); + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True(TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/dummy/test"), url)); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new DummyCreateParams + { + BaseOverlay = new() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + }, + ExtensionConfig = new RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + Extensions = + [ + new ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 5, + MinConfidence = 95, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new ExtensionItemAIAutoDescription(), + new ExtensionItemAITasks( + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + new ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }, + ] + ), + new SavedExtension("ext_abc123"), + ], + GetImageAttributesOptions = new() + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + ExpiresIn = 0, + QueryParameters = new Dictionary() { { "foo", "string" } }, + Signed = true, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + TransformationPosition = TransformationPosition.Path, + DeviceBreakpoints = [640, 750, 828, 1080, 1200, 1920, 2048, 3840], + ImageBreakpoints = [16, 32, 48, 64, 96, 128, 256, 384], + Sizes = "(min-width: 768px) 50vw, 100vw", + Width = 400, + }, + ImageOverlay = new() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = Encoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + OverlayPosition = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + OverlayTiming = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + ResponsiveImageAttributes = new() + { + Src = "https://ik.imagekit.io/demo/image.jpg?tr=w-3840", + Sizes = "100vw", + SrcSet = + "https://ik.imagekit.io/demo/image.jpg?tr=w-640 640w, https://ik.imagekit.io/demo/image.jpg?tr=w-1080 1080w, https://ik.imagekit.io/demo/image.jpg?tr=w-1920 1920w", + Width = 400, + }, + SavedExtensions = new() + { + ID = "ext_abc123", + Config = new RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Description = "Analyzes vehicle images for type, condition, and quality assessment", + Name = "Car Quality Analysis", + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + }, + SolidColorOverlay = new() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Color = "color", + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }, + ], + }, + SolidColorOverlayTransformation = new() + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }, + SrcOptions = new() + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + ExpiresIn = 0, + QueryParameters = new Dictionary() { { "foo", "string" } }, + Signed = true, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + TransformationPosition = TransformationPosition.Path, + }, + StreamingResolution = StreamingResolution.V240, + SubtitleOverlay = new() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = SubtitleOverlaySubtitleOverlayEncoding.Auto, + Transformation = + [ + new() + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }, + ], + }, + SubtitleOverlayTransformation = new() + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }, + TextOverlay = new() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + TextOverlayTransformation = new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + Transformation = new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + TransformationPosition = TransformationPosition.Path, + VideoOverlay = new() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = VideoOverlayVideoOverlayEncoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }, + }; + + DummyCreateParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/ExtensionConfigTest.cs b/src/Imagekit.Tests/Models/ExtensionConfigTest.cs new file mode 100644 index 00000000..9e825d37 --- /dev/null +++ b/src/Imagekit.Tests/Models/ExtensionConfigTest.cs @@ -0,0 +1,3149 @@ +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class ExtensionConfigTest : TestBase +{ + [Fact] + public void RemoveBgValidationWorks() + { + ExtensionConfig value = new RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }; + value.Validate(); + } + + [Fact] + public void AutoTaggingExtensionValidationWorks() + { + ExtensionConfig value = new AutoTaggingExtension() + { + MaxTags = 0, + MinConfidence = 0, + Name = Name.GoogleAutoTagging, + }; + value.Validate(); + } + + [Fact] + public void AIAutoDescriptionValidationWorks() + { + ExtensionConfig value = new AIAutoDescription(); + value.Validate(); + } + + [Fact] + public void AITasksValidationWorks() + { + ExtensionConfig value = new AITasks( + [ + new SelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + ] + ); + value.Validate(); + } + + [Fact] + public void RemoveBgSerializationRoundtripWorks() + { + ExtensionConfig value = new RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void AutoTaggingExtensionSerializationRoundtripWorks() + { + ExtensionConfig value = new AutoTaggingExtension() + { + MaxTags = 0, + MinConfidence = 0, + Name = Name.GoogleAutoTagging, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void AIAutoDescriptionSerializationRoundtripWorks() + { + ExtensionConfig value = new AIAutoDescription(); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void AITasksSerializationRoundtripWorks() + { + ExtensionConfig value = new AITasks( + [ + new SelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + ] + ); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class RemoveBgTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new RemoveBg + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }; + + JsonElement expectedName = JsonSerializer.SerializeToElement("remove-bg"); + Options expectedOptions = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }; + + Assert.True(JsonElement.DeepEquals(expectedName, model.Name)); + Assert.Equal(expectedOptions, model.Options); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new RemoveBg + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new RemoveBg + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + JsonElement expectedName = JsonSerializer.SerializeToElement("remove-bg"); + Options expectedOptions = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }; + + Assert.True(JsonElement.DeepEquals(expectedName, deserialized.Name)); + Assert.Equal(expectedOptions, deserialized.Options); + } + + [Fact] + public void Validation_Works() + { + var model = new RemoveBg + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new RemoveBg { }; + + Assert.Null(model.Options); + Assert.False(model.RawData.ContainsKey("options")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new RemoveBg { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new RemoveBg + { + // Null should be interpreted as omitted for these properties + Options = null, + }; + + Assert.Null(model.Options); + Assert.False(model.RawData.ContainsKey("options")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new RemoveBg + { + // Null should be interpreted as omitted for these properties + Options = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new RemoveBg + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }; + + RemoveBg copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class OptionsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Options + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }; + + bool expectedAddShadow = true; + string expectedBgColor = "bg_color"; + string expectedBgImageUrl = "bg_image_url"; + bool expectedSemitransparency = true; + + Assert.Equal(expectedAddShadow, model.AddShadow); + Assert.Equal(expectedBgColor, model.BgColor); + Assert.Equal(expectedBgImageUrl, model.BgImageUrl); + Assert.Equal(expectedSemitransparency, model.Semitransparency); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Options + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Options + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + bool expectedAddShadow = true; + string expectedBgColor = "bg_color"; + string expectedBgImageUrl = "bg_image_url"; + bool expectedSemitransparency = true; + + Assert.Equal(expectedAddShadow, deserialized.AddShadow); + Assert.Equal(expectedBgColor, deserialized.BgColor); + Assert.Equal(expectedBgImageUrl, deserialized.BgImageUrl); + Assert.Equal(expectedSemitransparency, deserialized.Semitransparency); + } + + [Fact] + public void Validation_Works() + { + var model = new Options + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Options { }; + + Assert.Null(model.AddShadow); + Assert.False(model.RawData.ContainsKey("add_shadow")); + Assert.Null(model.BgColor); + Assert.False(model.RawData.ContainsKey("bg_color")); + Assert.Null(model.BgImageUrl); + Assert.False(model.RawData.ContainsKey("bg_image_url")); + Assert.Null(model.Semitransparency); + Assert.False(model.RawData.ContainsKey("semitransparency")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Options { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Options + { + // Null should be interpreted as omitted for these properties + AddShadow = null, + BgColor = null, + BgImageUrl = null, + Semitransparency = null, + }; + + Assert.Null(model.AddShadow); + Assert.False(model.RawData.ContainsKey("add_shadow")); + Assert.Null(model.BgColor); + Assert.False(model.RawData.ContainsKey("bg_color")); + Assert.Null(model.BgImageUrl); + Assert.False(model.RawData.ContainsKey("bg_image_url")); + Assert.Null(model.Semitransparency); + Assert.False(model.RawData.ContainsKey("semitransparency")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Options + { + // Null should be interpreted as omitted for these properties + AddShadow = null, + BgColor = null, + BgImageUrl = null, + Semitransparency = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Options + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }; + + Options copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class AutoTaggingExtensionTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new AutoTaggingExtension + { + MaxTags = 0, + MinConfidence = 0, + Name = Name.GoogleAutoTagging, + }; + + long expectedMaxTags = 0; + long expectedMinConfidence = 0; + ApiEnum expectedName = Name.GoogleAutoTagging; + + Assert.Equal(expectedMaxTags, model.MaxTags); + Assert.Equal(expectedMinConfidence, model.MinConfidence); + Assert.Equal(expectedName, model.Name); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new AutoTaggingExtension + { + MaxTags = 0, + MinConfidence = 0, + Name = Name.GoogleAutoTagging, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new AutoTaggingExtension + { + MaxTags = 0, + MinConfidence = 0, + Name = Name.GoogleAutoTagging, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + long expectedMaxTags = 0; + long expectedMinConfidence = 0; + ApiEnum expectedName = Name.GoogleAutoTagging; + + Assert.Equal(expectedMaxTags, deserialized.MaxTags); + Assert.Equal(expectedMinConfidence, deserialized.MinConfidence); + Assert.Equal(expectedName, deserialized.Name); + } + + [Fact] + public void Validation_Works() + { + var model = new AutoTaggingExtension + { + MaxTags = 0, + MinConfidence = 0, + Name = Name.GoogleAutoTagging, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new AutoTaggingExtension + { + MaxTags = 0, + MinConfidence = 0, + Name = Name.GoogleAutoTagging, + }; + + AutoTaggingExtension copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class NameTest : TestBase +{ + [Theory] + [InlineData(Name.GoogleAutoTagging)] + [InlineData(Name.AwsAutoTagging)] + public void Validation_Works(Name rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Name.GoogleAutoTagging)] + [InlineData(Name.AwsAutoTagging)] + public void SerializationRoundtrip_Works(Name rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class AIAutoDescriptionTest : TestBase +{ + [Fact] + public void DefaultValidation_Works() + { + var constant = new AIAutoDescription(); + constant.Validate(); + } + + [Fact] + public void ValidConstantValidation_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.Deserialize( + """ + { + "name": "ai-auto-description" + } + """ + ), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + constant.Validate(); + } + + [Fact] + public void InvalidConstantValidationThrows_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + Assert.Throws(() => constant.Validate()); + } + + [Fact] + public void DefaultRoundtrip_Works() + { + var constant = new AIAutoDescription(); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void ValidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.Deserialize( + """ + { + "name": "ai-auto-description" + } + """ + ), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void InvalidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } +} + +public class AITasksTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new AITasks + { + Tasks = + [ + new SelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + ], + }; + + JsonElement expectedName = JsonSerializer.SerializeToElement("ai-tasks"); + List expectedTasks = + [ + new SelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + ]; + + Assert.True(JsonElement.DeepEquals(expectedName, model.Name)); + Assert.Equal(expectedTasks.Count, model.Tasks.Count); + for (int i = 0; i < expectedTasks.Count; i++) + { + Assert.Equal(expectedTasks[i], model.Tasks[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new AITasks + { + Tasks = + [ + new SelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + ], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new AITasks + { + Tasks = + [ + new SelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + ], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + JsonElement expectedName = JsonSerializer.SerializeToElement("ai-tasks"); + List expectedTasks = + [ + new SelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + ]; + + Assert.True(JsonElement.DeepEquals(expectedName, deserialized.Name)); + Assert.Equal(expectedTasks.Count, deserialized.Tasks.Count); + for (int i = 0; i < expectedTasks.Count; i++) + { + Assert.Equal(expectedTasks[i], deserialized.Tasks[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new AITasks + { + Tasks = + [ + new SelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + ], + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new AITasks + { + Tasks = + [ + new SelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + ], + }; + + AITasks copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class TaskTest : TestBase +{ + [Fact] + public void SelectTagsValidationWorks() + { + Task value = new SelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }; + value.Validate(); + } + + [Fact] + public void SelectMetadataValidationWorks() + { + Task value = new SelectMetadata() + { + Field = "primary_color", + Instruction = "What is the primary color of the clothing?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["red", "blue", "green", "black", "white"], + }; + value.Validate(); + } + + [Fact] + public void YesNoValidationWorks() + { + Task value = new YesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }; + value.Validate(); + } + + [Fact] + public void SelectTagsSerializationRoundtripWorks() + { + Task value = new SelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void SelectMetadataSerializationRoundtripWorks() + { + Task value = new SelectMetadata() + { + Field = "primary_color", + Instruction = "What is the primary color of the clothing?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["red", "blue", "green", "black", "white"], + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void YesNoSerializationRoundtripWorks() + { + Task value = new YesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class SelectTagsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SelectTags + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }; + + string expectedInstruction = "What types of clothing items are visible in this image?"; + JsonElement expectedType = JsonSerializer.SerializeToElement("select_tags"); + long expectedMaxSelections = 1; + long expectedMinSelections = 0; + List expectedVocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"]; + + Assert.Equal(expectedInstruction, model.Instruction); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedMaxSelections, model.MaxSelections); + Assert.Equal(expectedMinSelections, model.MinSelections); + Assert.NotNull(model.Vocabulary); + Assert.Equal(expectedVocabulary.Count, model.Vocabulary.Count); + for (int i = 0; i < expectedVocabulary.Count; i++) + { + Assert.Equal(expectedVocabulary[i], model.Vocabulary[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SelectTags + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SelectTags + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedInstruction = "What types of clothing items are visible in this image?"; + JsonElement expectedType = JsonSerializer.SerializeToElement("select_tags"); + long expectedMaxSelections = 1; + long expectedMinSelections = 0; + List expectedVocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"]; + + Assert.Equal(expectedInstruction, deserialized.Instruction); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedMaxSelections, deserialized.MaxSelections); + Assert.Equal(expectedMinSelections, deserialized.MinSelections); + Assert.NotNull(deserialized.Vocabulary); + Assert.Equal(expectedVocabulary.Count, deserialized.Vocabulary.Count); + for (int i = 0; i < expectedVocabulary.Count; i++) + { + Assert.Equal(expectedVocabulary[i], deserialized.Vocabulary[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new SelectTags + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new SelectTags + { + Instruction = "What types of clothing items are visible in this image?", + }; + + Assert.Null(model.MaxSelections); + Assert.False(model.RawData.ContainsKey("max_selections")); + Assert.Null(model.MinSelections); + Assert.False(model.RawData.ContainsKey("min_selections")); + Assert.Null(model.Vocabulary); + Assert.False(model.RawData.ContainsKey("vocabulary")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new SelectTags + { + Instruction = "What types of clothing items are visible in this image?", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new SelectTags + { + Instruction = "What types of clothing items are visible in this image?", + + // Null should be interpreted as omitted for these properties + MaxSelections = null, + MinSelections = null, + Vocabulary = null, + }; + + Assert.Null(model.MaxSelections); + Assert.False(model.RawData.ContainsKey("max_selections")); + Assert.Null(model.MinSelections); + Assert.False(model.RawData.ContainsKey("min_selections")); + Assert.Null(model.Vocabulary); + Assert.False(model.RawData.ContainsKey("vocabulary")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new SelectTags + { + Instruction = "What types of clothing items are visible in this image?", + + // Null should be interpreted as omitted for these properties + MaxSelections = null, + MinSelections = null, + Vocabulary = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SelectTags + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }; + + SelectTags copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class SelectMetadataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SelectMetadata + { + Field = "primary_color", + Instruction = "What is the primary color of the clothing?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["red", "blue", "green", "black", "white"], + }; + + string expectedField = "primary_color"; + string expectedInstruction = "What is the primary color of the clothing?"; + JsonElement expectedType = JsonSerializer.SerializeToElement("select_metadata"); + long expectedMaxSelections = 1; + long expectedMinSelections = 0; + List expectedVocabulary = ["red", "blue", "green", "black", "white"]; + + Assert.Equal(expectedField, model.Field); + Assert.Equal(expectedInstruction, model.Instruction); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedMaxSelections, model.MaxSelections); + Assert.Equal(expectedMinSelections, model.MinSelections); + Assert.NotNull(model.Vocabulary); + Assert.Equal(expectedVocabulary.Count, model.Vocabulary.Count); + for (int i = 0; i < expectedVocabulary.Count; i++) + { + Assert.Equal(expectedVocabulary[i], model.Vocabulary[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SelectMetadata + { + Field = "primary_color", + Instruction = "What is the primary color of the clothing?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["red", "blue", "green", "black", "white"], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SelectMetadata + { + Field = "primary_color", + Instruction = "What is the primary color of the clothing?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["red", "blue", "green", "black", "white"], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedField = "primary_color"; + string expectedInstruction = "What is the primary color of the clothing?"; + JsonElement expectedType = JsonSerializer.SerializeToElement("select_metadata"); + long expectedMaxSelections = 1; + long expectedMinSelections = 0; + List expectedVocabulary = ["red", "blue", "green", "black", "white"]; + + Assert.Equal(expectedField, deserialized.Field); + Assert.Equal(expectedInstruction, deserialized.Instruction); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedMaxSelections, deserialized.MaxSelections); + Assert.Equal(expectedMinSelections, deserialized.MinSelections); + Assert.NotNull(deserialized.Vocabulary); + Assert.Equal(expectedVocabulary.Count, deserialized.Vocabulary.Count); + for (int i = 0; i < expectedVocabulary.Count; i++) + { + Assert.Equal(expectedVocabulary[i], deserialized.Vocabulary[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new SelectMetadata + { + Field = "primary_color", + Instruction = "What is the primary color of the clothing?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["red", "blue", "green", "black", "white"], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new SelectMetadata + { + Field = "primary_color", + Instruction = "What is the primary color of the clothing?", + }; + + Assert.Null(model.MaxSelections); + Assert.False(model.RawData.ContainsKey("max_selections")); + Assert.Null(model.MinSelections); + Assert.False(model.RawData.ContainsKey("min_selections")); + Assert.Null(model.Vocabulary); + Assert.False(model.RawData.ContainsKey("vocabulary")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new SelectMetadata + { + Field = "primary_color", + Instruction = "What is the primary color of the clothing?", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new SelectMetadata + { + Field = "primary_color", + Instruction = "What is the primary color of the clothing?", + + // Null should be interpreted as omitted for these properties + MaxSelections = null, + MinSelections = null, + Vocabulary = null, + }; + + Assert.Null(model.MaxSelections); + Assert.False(model.RawData.ContainsKey("max_selections")); + Assert.Null(model.MinSelections); + Assert.False(model.RawData.ContainsKey("min_selections")); + Assert.Null(model.Vocabulary); + Assert.False(model.RawData.ContainsKey("vocabulary")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new SelectMetadata + { + Field = "primary_color", + Instruction = "What is the primary color of the clothing?", + + // Null should be interpreted as omitted for these properties + MaxSelections = null, + MinSelections = null, + Vocabulary = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SelectMetadata + { + Field = "primary_color", + Instruction = "What is the primary color of the clothing?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["red", "blue", "green", "black", "white"], + }; + + SelectMetadata copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class VocabularyTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + Vocabulary value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + Vocabulary value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + Vocabulary value = true; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + Vocabulary value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + Vocabulary value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + Vocabulary value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class YesNoTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new YesNo + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }; + + string expectedInstruction = "Is this a luxury or high-end fashion item?"; + JsonElement expectedType = JsonSerializer.SerializeToElement("yes_no"); + OnNo expectedOnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + OnUnknown expectedOnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + OnYes expectedOnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + Assert.Equal(expectedInstruction, model.Instruction); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedOnNo, model.OnNo); + Assert.Equal(expectedOnUnknown, model.OnUnknown); + Assert.Equal(expectedOnYes, model.OnYes); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new YesNo + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new YesNo + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + Assert.NotNull(deserialized); + + string expectedInstruction = "Is this a luxury or high-end fashion item?"; + JsonElement expectedType = JsonSerializer.SerializeToElement("yes_no"); + OnNo expectedOnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + OnUnknown expectedOnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + OnYes expectedOnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + Assert.Equal(expectedInstruction, deserialized.Instruction); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedOnNo, deserialized.OnNo); + Assert.Equal(expectedOnUnknown, deserialized.OnUnknown); + Assert.Equal(expectedOnYes, deserialized.OnYes); + } + + [Fact] + public void Validation_Works() + { + var model = new YesNo + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new YesNo { Instruction = "Is this a luxury or high-end fashion item?" }; + + Assert.Null(model.OnNo); + Assert.False(model.RawData.ContainsKey("on_no")); + Assert.Null(model.OnUnknown); + Assert.False(model.RawData.ContainsKey("on_unknown")); + Assert.Null(model.OnYes); + Assert.False(model.RawData.ContainsKey("on_yes")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new YesNo { Instruction = "Is this a luxury or high-end fashion item?" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new YesNo + { + Instruction = "Is this a luxury or high-end fashion item?", + + // Null should be interpreted as omitted for these properties + OnNo = null, + OnUnknown = null, + OnYes = null, + }; + + Assert.Null(model.OnNo); + Assert.False(model.RawData.ContainsKey("on_no")); + Assert.Null(model.OnUnknown); + Assert.False(model.RawData.ContainsKey("on_unknown")); + Assert.Null(model.OnYes); + Assert.False(model.RawData.ContainsKey("on_yes")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new YesNo + { + Instruction = "Is this a luxury or high-end fashion item?", + + // Null should be interpreted as omitted for these properties + OnNo = null, + OnUnknown = null, + OnYes = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new YesNo + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }; + + YesNo copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class OnNoTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new OnNo + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + List expectedAddTags = ["luxury", "premium"]; + List expectedRemoveTags = ["budget", "affordable"]; + List expectedSetMetadata = + [ + new() { Field = "price_range", Value = "premium" }, + ]; + List expectedUnsetMetadata = [new("price_range")]; + + Assert.NotNull(model.AddTags); + Assert.Equal(expectedAddTags.Count, model.AddTags.Count); + for (int i = 0; i < expectedAddTags.Count; i++) + { + Assert.Equal(expectedAddTags[i], model.AddTags[i]); + } + Assert.NotNull(model.RemoveTags); + Assert.Equal(expectedRemoveTags.Count, model.RemoveTags.Count); + for (int i = 0; i < expectedRemoveTags.Count; i++) + { + Assert.Equal(expectedRemoveTags[i], model.RemoveTags[i]); + } + Assert.NotNull(model.SetMetadata); + Assert.Equal(expectedSetMetadata.Count, model.SetMetadata.Count); + for (int i = 0; i < expectedSetMetadata.Count; i++) + { + Assert.Equal(expectedSetMetadata[i], model.SetMetadata[i]); + } + Assert.NotNull(model.UnsetMetadata); + Assert.Equal(expectedUnsetMetadata.Count, model.UnsetMetadata.Count); + for (int i = 0; i < expectedUnsetMetadata.Count; i++) + { + Assert.Equal(expectedUnsetMetadata[i], model.UnsetMetadata[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new OnNo + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new OnNo + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + Assert.NotNull(deserialized); + + List expectedAddTags = ["luxury", "premium"]; + List expectedRemoveTags = ["budget", "affordable"]; + List expectedSetMetadata = + [ + new() { Field = "price_range", Value = "premium" }, + ]; + List expectedUnsetMetadata = [new("price_range")]; + + Assert.NotNull(deserialized.AddTags); + Assert.Equal(expectedAddTags.Count, deserialized.AddTags.Count); + for (int i = 0; i < expectedAddTags.Count; i++) + { + Assert.Equal(expectedAddTags[i], deserialized.AddTags[i]); + } + Assert.NotNull(deserialized.RemoveTags); + Assert.Equal(expectedRemoveTags.Count, deserialized.RemoveTags.Count); + for (int i = 0; i < expectedRemoveTags.Count; i++) + { + Assert.Equal(expectedRemoveTags[i], deserialized.RemoveTags[i]); + } + Assert.NotNull(deserialized.SetMetadata); + Assert.Equal(expectedSetMetadata.Count, deserialized.SetMetadata.Count); + for (int i = 0; i < expectedSetMetadata.Count; i++) + { + Assert.Equal(expectedSetMetadata[i], deserialized.SetMetadata[i]); + } + Assert.NotNull(deserialized.UnsetMetadata); + Assert.Equal(expectedUnsetMetadata.Count, deserialized.UnsetMetadata.Count); + for (int i = 0; i < expectedUnsetMetadata.Count; i++) + { + Assert.Equal(expectedUnsetMetadata[i], deserialized.UnsetMetadata[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new OnNo + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new OnNo { }; + + Assert.Null(model.AddTags); + Assert.False(model.RawData.ContainsKey("add_tags")); + Assert.Null(model.RemoveTags); + Assert.False(model.RawData.ContainsKey("remove_tags")); + Assert.Null(model.SetMetadata); + Assert.False(model.RawData.ContainsKey("set_metadata")); + Assert.Null(model.UnsetMetadata); + Assert.False(model.RawData.ContainsKey("unset_metadata")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new OnNo { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new OnNo + { + // Null should be interpreted as omitted for these properties + AddTags = null, + RemoveTags = null, + SetMetadata = null, + UnsetMetadata = null, + }; + + Assert.Null(model.AddTags); + Assert.False(model.RawData.ContainsKey("add_tags")); + Assert.Null(model.RemoveTags); + Assert.False(model.RawData.ContainsKey("remove_tags")); + Assert.Null(model.SetMetadata); + Assert.False(model.RawData.ContainsKey("set_metadata")); + Assert.Null(model.UnsetMetadata); + Assert.False(model.RawData.ContainsKey("unset_metadata")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new OnNo + { + // Null should be interpreted as omitted for these properties + AddTags = null, + RemoveTags = null, + SetMetadata = null, + UnsetMetadata = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new OnNo + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + OnNo copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class SetMetadataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SetMetadata { Field = "field", Value = "string" }; + + string expectedField = "field"; + SetMetadataValue expectedValue = "string"; + + Assert.Equal(expectedField, model.Field); + Assert.Equal(expectedValue, model.Value); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SetMetadata { Field = "field", Value = "string" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SetMetadata { Field = "field", Value = "string" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedField = "field"; + SetMetadataValue expectedValue = "string"; + + Assert.Equal(expectedField, deserialized.Field); + Assert.Equal(expectedValue, deserialized.Value); + } + + [Fact] + public void Validation_Works() + { + var model = new SetMetadata { Field = "field", Value = "string" }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SetMetadata { Field = "field", Value = "string" }; + + SetMetadata copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class SetMetadataValueTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + SetMetadataValue value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + SetMetadataValue value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + SetMetadataValue value = true; + value.Validate(); + } + + [Fact] + public void MixedValidationWorks() + { + SetMetadataValue value = new([new MetadataValueItem("string")]); + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + SetMetadataValue value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + SetMetadataValue value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + SetMetadataValue value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void MixedSerializationRoundtripWorks() + { + SetMetadataValue value = new([new MetadataValueItem("string")]); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class MetadataValueItemTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + MetadataValueItem value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + MetadataValueItem value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + MetadataValueItem value = true; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + MetadataValueItem value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + MetadataValueItem value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + MetadataValueItem value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class UnsetMetadataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new UnsetMetadata { Field = "field" }; + + string expectedField = "field"; + + Assert.Equal(expectedField, model.Field); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new UnsetMetadata { Field = "field" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new UnsetMetadata { Field = "field" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedField = "field"; + + Assert.Equal(expectedField, deserialized.Field); + } + + [Fact] + public void Validation_Works() + { + var model = new UnsetMetadata { Field = "field" }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new UnsetMetadata { Field = "field" }; + + UnsetMetadata copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class OnUnknownTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new OnUnknown + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + List expectedAddTags = ["luxury", "premium"]; + List expectedRemoveTags = ["budget", "affordable"]; + List expectedSetMetadata = + [ + new() { Field = "price_range", Value = "premium" }, + ]; + List expectedUnsetMetadata = [new("price_range")]; + + Assert.NotNull(model.AddTags); + Assert.Equal(expectedAddTags.Count, model.AddTags.Count); + for (int i = 0; i < expectedAddTags.Count; i++) + { + Assert.Equal(expectedAddTags[i], model.AddTags[i]); + } + Assert.NotNull(model.RemoveTags); + Assert.Equal(expectedRemoveTags.Count, model.RemoveTags.Count); + for (int i = 0; i < expectedRemoveTags.Count; i++) + { + Assert.Equal(expectedRemoveTags[i], model.RemoveTags[i]); + } + Assert.NotNull(model.SetMetadata); + Assert.Equal(expectedSetMetadata.Count, model.SetMetadata.Count); + for (int i = 0; i < expectedSetMetadata.Count; i++) + { + Assert.Equal(expectedSetMetadata[i], model.SetMetadata[i]); + } + Assert.NotNull(model.UnsetMetadata); + Assert.Equal(expectedUnsetMetadata.Count, model.UnsetMetadata.Count); + for (int i = 0; i < expectedUnsetMetadata.Count; i++) + { + Assert.Equal(expectedUnsetMetadata[i], model.UnsetMetadata[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new OnUnknown + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new OnUnknown + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + List expectedAddTags = ["luxury", "premium"]; + List expectedRemoveTags = ["budget", "affordable"]; + List expectedSetMetadata = + [ + new() { Field = "price_range", Value = "premium" }, + ]; + List expectedUnsetMetadata = [new("price_range")]; + + Assert.NotNull(deserialized.AddTags); + Assert.Equal(expectedAddTags.Count, deserialized.AddTags.Count); + for (int i = 0; i < expectedAddTags.Count; i++) + { + Assert.Equal(expectedAddTags[i], deserialized.AddTags[i]); + } + Assert.NotNull(deserialized.RemoveTags); + Assert.Equal(expectedRemoveTags.Count, deserialized.RemoveTags.Count); + for (int i = 0; i < expectedRemoveTags.Count; i++) + { + Assert.Equal(expectedRemoveTags[i], deserialized.RemoveTags[i]); + } + Assert.NotNull(deserialized.SetMetadata); + Assert.Equal(expectedSetMetadata.Count, deserialized.SetMetadata.Count); + for (int i = 0; i < expectedSetMetadata.Count; i++) + { + Assert.Equal(expectedSetMetadata[i], deserialized.SetMetadata[i]); + } + Assert.NotNull(deserialized.UnsetMetadata); + Assert.Equal(expectedUnsetMetadata.Count, deserialized.UnsetMetadata.Count); + for (int i = 0; i < expectedUnsetMetadata.Count; i++) + { + Assert.Equal(expectedUnsetMetadata[i], deserialized.UnsetMetadata[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new OnUnknown + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new OnUnknown { }; + + Assert.Null(model.AddTags); + Assert.False(model.RawData.ContainsKey("add_tags")); + Assert.Null(model.RemoveTags); + Assert.False(model.RawData.ContainsKey("remove_tags")); + Assert.Null(model.SetMetadata); + Assert.False(model.RawData.ContainsKey("set_metadata")); + Assert.Null(model.UnsetMetadata); + Assert.False(model.RawData.ContainsKey("unset_metadata")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new OnUnknown { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new OnUnknown + { + // Null should be interpreted as omitted for these properties + AddTags = null, + RemoveTags = null, + SetMetadata = null, + UnsetMetadata = null, + }; + + Assert.Null(model.AddTags); + Assert.False(model.RawData.ContainsKey("add_tags")); + Assert.Null(model.RemoveTags); + Assert.False(model.RawData.ContainsKey("remove_tags")); + Assert.Null(model.SetMetadata); + Assert.False(model.RawData.ContainsKey("set_metadata")); + Assert.Null(model.UnsetMetadata); + Assert.False(model.RawData.ContainsKey("unset_metadata")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new OnUnknown + { + // Null should be interpreted as omitted for these properties + AddTags = null, + RemoveTags = null, + SetMetadata = null, + UnsetMetadata = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new OnUnknown + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + OnUnknown copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class OnUnknownSetMetadataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new OnUnknownSetMetadata { Field = "field", Value = "string" }; + + string expectedField = "field"; + OnUnknownSetMetadataValue expectedValue = "string"; + + Assert.Equal(expectedField, model.Field); + Assert.Equal(expectedValue, model.Value); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new OnUnknownSetMetadata { Field = "field", Value = "string" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new OnUnknownSetMetadata { Field = "field", Value = "string" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedField = "field"; + OnUnknownSetMetadataValue expectedValue = "string"; + + Assert.Equal(expectedField, deserialized.Field); + Assert.Equal(expectedValue, deserialized.Value); + } + + [Fact] + public void Validation_Works() + { + var model = new OnUnknownSetMetadata { Field = "field", Value = "string" }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new OnUnknownSetMetadata { Field = "field", Value = "string" }; + + OnUnknownSetMetadata copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class OnUnknownSetMetadataValueTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + OnUnknownSetMetadataValue value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + OnUnknownSetMetadataValue value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + OnUnknownSetMetadataValue value = true; + value.Validate(); + } + + [Fact] + public void MixedValidationWorks() + { + OnUnknownSetMetadataValue value = new( + [new OnUnknownSetMetadataValueMetadataValueItem("string")] + ); + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + OnUnknownSetMetadataValue value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + OnUnknownSetMetadataValue value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + OnUnknownSetMetadataValue value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void MixedSerializationRoundtripWorks() + { + OnUnknownSetMetadataValue value = new( + [new OnUnknownSetMetadataValueMetadataValueItem("string")] + ); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class OnUnknownSetMetadataValueMetadataValueItemTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + OnUnknownSetMetadataValueMetadataValueItem value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + OnUnknownSetMetadataValueMetadataValueItem value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + OnUnknownSetMetadataValueMetadataValueItem value = true; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + OnUnknownSetMetadataValueMetadataValueItem value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + OnUnknownSetMetadataValueMetadataValueItem value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + OnUnknownSetMetadataValueMetadataValueItem value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class OnUnknownUnsetMetadataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new OnUnknownUnsetMetadata { Field = "field" }; + + string expectedField = "field"; + + Assert.Equal(expectedField, model.Field); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new OnUnknownUnsetMetadata { Field = "field" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new OnUnknownUnsetMetadata { Field = "field" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedField = "field"; + + Assert.Equal(expectedField, deserialized.Field); + } + + [Fact] + public void Validation_Works() + { + var model = new OnUnknownUnsetMetadata { Field = "field" }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new OnUnknownUnsetMetadata { Field = "field" }; + + OnUnknownUnsetMetadata copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class OnYesTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new OnYes + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + List expectedAddTags = ["luxury", "premium"]; + List expectedRemoveTags = ["budget", "affordable"]; + List expectedSetMetadata = + [ + new() { Field = "price_range", Value = "premium" }, + ]; + List expectedUnsetMetadata = [new("price_range")]; + + Assert.NotNull(model.AddTags); + Assert.Equal(expectedAddTags.Count, model.AddTags.Count); + for (int i = 0; i < expectedAddTags.Count; i++) + { + Assert.Equal(expectedAddTags[i], model.AddTags[i]); + } + Assert.NotNull(model.RemoveTags); + Assert.Equal(expectedRemoveTags.Count, model.RemoveTags.Count); + for (int i = 0; i < expectedRemoveTags.Count; i++) + { + Assert.Equal(expectedRemoveTags[i], model.RemoveTags[i]); + } + Assert.NotNull(model.SetMetadata); + Assert.Equal(expectedSetMetadata.Count, model.SetMetadata.Count); + for (int i = 0; i < expectedSetMetadata.Count; i++) + { + Assert.Equal(expectedSetMetadata[i], model.SetMetadata[i]); + } + Assert.NotNull(model.UnsetMetadata); + Assert.Equal(expectedUnsetMetadata.Count, model.UnsetMetadata.Count); + for (int i = 0; i < expectedUnsetMetadata.Count; i++) + { + Assert.Equal(expectedUnsetMetadata[i], model.UnsetMetadata[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new OnYes + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new OnYes + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + Assert.NotNull(deserialized); + + List expectedAddTags = ["luxury", "premium"]; + List expectedRemoveTags = ["budget", "affordable"]; + List expectedSetMetadata = + [ + new() { Field = "price_range", Value = "premium" }, + ]; + List expectedUnsetMetadata = [new("price_range")]; + + Assert.NotNull(deserialized.AddTags); + Assert.Equal(expectedAddTags.Count, deserialized.AddTags.Count); + for (int i = 0; i < expectedAddTags.Count; i++) + { + Assert.Equal(expectedAddTags[i], deserialized.AddTags[i]); + } + Assert.NotNull(deserialized.RemoveTags); + Assert.Equal(expectedRemoveTags.Count, deserialized.RemoveTags.Count); + for (int i = 0; i < expectedRemoveTags.Count; i++) + { + Assert.Equal(expectedRemoveTags[i], deserialized.RemoveTags[i]); + } + Assert.NotNull(deserialized.SetMetadata); + Assert.Equal(expectedSetMetadata.Count, deserialized.SetMetadata.Count); + for (int i = 0; i < expectedSetMetadata.Count; i++) + { + Assert.Equal(expectedSetMetadata[i], deserialized.SetMetadata[i]); + } + Assert.NotNull(deserialized.UnsetMetadata); + Assert.Equal(expectedUnsetMetadata.Count, deserialized.UnsetMetadata.Count); + for (int i = 0; i < expectedUnsetMetadata.Count; i++) + { + Assert.Equal(expectedUnsetMetadata[i], deserialized.UnsetMetadata[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new OnYes + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new OnYes { }; + + Assert.Null(model.AddTags); + Assert.False(model.RawData.ContainsKey("add_tags")); + Assert.Null(model.RemoveTags); + Assert.False(model.RawData.ContainsKey("remove_tags")); + Assert.Null(model.SetMetadata); + Assert.False(model.RawData.ContainsKey("set_metadata")); + Assert.Null(model.UnsetMetadata); + Assert.False(model.RawData.ContainsKey("unset_metadata")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new OnYes { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new OnYes + { + // Null should be interpreted as omitted for these properties + AddTags = null, + RemoveTags = null, + SetMetadata = null, + UnsetMetadata = null, + }; + + Assert.Null(model.AddTags); + Assert.False(model.RawData.ContainsKey("add_tags")); + Assert.Null(model.RemoveTags); + Assert.False(model.RawData.ContainsKey("remove_tags")); + Assert.Null(model.SetMetadata); + Assert.False(model.RawData.ContainsKey("set_metadata")); + Assert.Null(model.UnsetMetadata); + Assert.False(model.RawData.ContainsKey("unset_metadata")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new OnYes + { + // Null should be interpreted as omitted for these properties + AddTags = null, + RemoveTags = null, + SetMetadata = null, + UnsetMetadata = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new OnYes + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + OnYes copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class OnYesSetMetadataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new OnYesSetMetadata { Field = "field", Value = "string" }; + + string expectedField = "field"; + OnYesSetMetadataValue expectedValue = "string"; + + Assert.Equal(expectedField, model.Field); + Assert.Equal(expectedValue, model.Value); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new OnYesSetMetadata { Field = "field", Value = "string" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new OnYesSetMetadata { Field = "field", Value = "string" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedField = "field"; + OnYesSetMetadataValue expectedValue = "string"; + + Assert.Equal(expectedField, deserialized.Field); + Assert.Equal(expectedValue, deserialized.Value); + } + + [Fact] + public void Validation_Works() + { + var model = new OnYesSetMetadata { Field = "field", Value = "string" }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new OnYesSetMetadata { Field = "field", Value = "string" }; + + OnYesSetMetadata copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class OnYesSetMetadataValueTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + OnYesSetMetadataValue value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + OnYesSetMetadataValue value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + OnYesSetMetadataValue value = true; + value.Validate(); + } + + [Fact] + public void MixedValidationWorks() + { + OnYesSetMetadataValue value = new([new OnYesSetMetadataValueMetadataValueItem("string")]); + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + OnYesSetMetadataValue value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + OnYesSetMetadataValue value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + OnYesSetMetadataValue value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void MixedSerializationRoundtripWorks() + { + OnYesSetMetadataValue value = new([new OnYesSetMetadataValueMetadataValueItem("string")]); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class OnYesSetMetadataValueMetadataValueItemTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + OnYesSetMetadataValueMetadataValueItem value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + OnYesSetMetadataValueMetadataValueItem value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + OnYesSetMetadataValueMetadataValueItem value = true; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + OnYesSetMetadataValueMetadataValueItem value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + OnYesSetMetadataValueMetadataValueItem value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + OnYesSetMetadataValueMetadataValueItem value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class OnYesUnsetMetadataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new OnYesUnsetMetadata { Field = "field" }; + + string expectedField = "field"; + + Assert.Equal(expectedField, model.Field); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new OnYesUnsetMetadata { Field = "field" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new OnYesUnsetMetadata { Field = "field" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedField = "field"; + + Assert.Equal(expectedField, deserialized.Field); + } + + [Fact] + public void Validation_Works() + { + var model = new OnYesUnsetMetadata { Field = "field" }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new OnYesUnsetMetadata { Field = "field" }; + + OnYesUnsetMetadata copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/ExtensionItemTest.cs b/src/Imagekit.Tests/Models/ExtensionItemTest.cs new file mode 100644 index 00000000..1c93edad --- /dev/null +++ b/src/Imagekit.Tests/Models/ExtensionItemTest.cs @@ -0,0 +1,3412 @@ +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class ExtensionItemTest : TestBase +{ + [Fact] + public void RemoveBgValidationWorks() + { + ExtensionItem value = new ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }; + value.Validate(); + } + + [Fact] + public void AutoTaggingExtensionValidationWorks() + { + ExtensionItem value = new ExtensionItemAutoTaggingExtension() + { + MaxTags = 0, + MinConfidence = 0, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }; + value.Validate(); + } + + [Fact] + public void AIAutoDescriptionValidationWorks() + { + ExtensionItem value = new ExtensionItemAIAutoDescription(); + value.Validate(); + } + + [Fact] + public void AITasksValidationWorks() + { + ExtensionItem value = new ExtensionItemAITasks( + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + ] + ); + value.Validate(); + } + + [Fact] + public void SavedExtensionValidationWorks() + { + ExtensionItem value = new SavedExtension("ext_abc123"); + value.Validate(); + } + + [Fact] + public void RemoveBgSerializationRoundtripWorks() + { + ExtensionItem value = new ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void AutoTaggingExtensionSerializationRoundtripWorks() + { + ExtensionItem value = new ExtensionItemAutoTaggingExtension() + { + MaxTags = 0, + MinConfidence = 0, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void AIAutoDescriptionSerializationRoundtripWorks() + { + ExtensionItem value = new ExtensionItemAIAutoDescription(); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void AITasksSerializationRoundtripWorks() + { + ExtensionItem value = new ExtensionItemAITasks( + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + ] + ); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void SavedExtensionSerializationRoundtripWorks() + { + ExtensionItem value = new SavedExtension("ext_abc123"); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class ExtensionItemRemoveBgTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExtensionItemRemoveBg + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }; + + JsonElement expectedName = JsonSerializer.SerializeToElement("remove-bg"); + ExtensionItemRemoveBgOptions expectedOptions = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }; + + Assert.True(JsonElement.DeepEquals(expectedName, model.Name)); + Assert.Equal(expectedOptions, model.Options); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExtensionItemRemoveBg + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExtensionItemRemoveBg + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + JsonElement expectedName = JsonSerializer.SerializeToElement("remove-bg"); + ExtensionItemRemoveBgOptions expectedOptions = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }; + + Assert.True(JsonElement.DeepEquals(expectedName, deserialized.Name)); + Assert.Equal(expectedOptions, deserialized.Options); + } + + [Fact] + public void Validation_Works() + { + var model = new ExtensionItemRemoveBg + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ExtensionItemRemoveBg { }; + + Assert.Null(model.Options); + Assert.False(model.RawData.ContainsKey("options")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new ExtensionItemRemoveBg { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ExtensionItemRemoveBg + { + // Null should be interpreted as omitted for these properties + Options = null, + }; + + Assert.Null(model.Options); + Assert.False(model.RawData.ContainsKey("options")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new ExtensionItemRemoveBg + { + // Null should be interpreted as omitted for these properties + Options = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExtensionItemRemoveBg + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }; + + ExtensionItemRemoveBg copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExtensionItemRemoveBgOptionsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExtensionItemRemoveBgOptions + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }; + + bool expectedAddShadow = true; + string expectedBgColor = "bg_color"; + string expectedBgImageUrl = "bg_image_url"; + bool expectedSemitransparency = true; + + Assert.Equal(expectedAddShadow, model.AddShadow); + Assert.Equal(expectedBgColor, model.BgColor); + Assert.Equal(expectedBgImageUrl, model.BgImageUrl); + Assert.Equal(expectedSemitransparency, model.Semitransparency); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExtensionItemRemoveBgOptions + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExtensionItemRemoveBgOptions + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + bool expectedAddShadow = true; + string expectedBgColor = "bg_color"; + string expectedBgImageUrl = "bg_image_url"; + bool expectedSemitransparency = true; + + Assert.Equal(expectedAddShadow, deserialized.AddShadow); + Assert.Equal(expectedBgColor, deserialized.BgColor); + Assert.Equal(expectedBgImageUrl, deserialized.BgImageUrl); + Assert.Equal(expectedSemitransparency, deserialized.Semitransparency); + } + + [Fact] + public void Validation_Works() + { + var model = new ExtensionItemRemoveBgOptions + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ExtensionItemRemoveBgOptions { }; + + Assert.Null(model.AddShadow); + Assert.False(model.RawData.ContainsKey("add_shadow")); + Assert.Null(model.BgColor); + Assert.False(model.RawData.ContainsKey("bg_color")); + Assert.Null(model.BgImageUrl); + Assert.False(model.RawData.ContainsKey("bg_image_url")); + Assert.Null(model.Semitransparency); + Assert.False(model.RawData.ContainsKey("semitransparency")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new ExtensionItemRemoveBgOptions { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ExtensionItemRemoveBgOptions + { + // Null should be interpreted as omitted for these properties + AddShadow = null, + BgColor = null, + BgImageUrl = null, + Semitransparency = null, + }; + + Assert.Null(model.AddShadow); + Assert.False(model.RawData.ContainsKey("add_shadow")); + Assert.Null(model.BgColor); + Assert.False(model.RawData.ContainsKey("bg_color")); + Assert.Null(model.BgImageUrl); + Assert.False(model.RawData.ContainsKey("bg_image_url")); + Assert.Null(model.Semitransparency); + Assert.False(model.RawData.ContainsKey("semitransparency")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new ExtensionItemRemoveBgOptions + { + // Null should be interpreted as omitted for these properties + AddShadow = null, + BgColor = null, + BgImageUrl = null, + Semitransparency = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExtensionItemRemoveBgOptions + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }; + + ExtensionItemRemoveBgOptions copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExtensionItemAutoTaggingExtensionTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExtensionItemAutoTaggingExtension + { + MaxTags = 0, + MinConfidence = 0, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }; + + long expectedMaxTags = 0; + long expectedMinConfidence = 0; + ApiEnum expectedName = + ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging; + + Assert.Equal(expectedMaxTags, model.MaxTags); + Assert.Equal(expectedMinConfidence, model.MinConfidence); + Assert.Equal(expectedName, model.Name); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExtensionItemAutoTaggingExtension + { + MaxTags = 0, + MinConfidence = 0, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExtensionItemAutoTaggingExtension + { + MaxTags = 0, + MinConfidence = 0, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + long expectedMaxTags = 0; + long expectedMinConfidence = 0; + ApiEnum expectedName = + ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging; + + Assert.Equal(expectedMaxTags, deserialized.MaxTags); + Assert.Equal(expectedMinConfidence, deserialized.MinConfidence); + Assert.Equal(expectedName, deserialized.Name); + } + + [Fact] + public void Validation_Works() + { + var model = new ExtensionItemAutoTaggingExtension + { + MaxTags = 0, + MinConfidence = 0, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExtensionItemAutoTaggingExtension + { + MaxTags = 0, + MinConfidence = 0, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }; + + ExtensionItemAutoTaggingExtension copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExtensionItemAutoTaggingExtensionNameTest : TestBase +{ + [Theory] + [InlineData(ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging)] + [InlineData(ExtensionItemAutoTaggingExtensionName.AwsAutoTagging)] + public void Validation_Works(ExtensionItemAutoTaggingExtensionName rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging)] + [InlineData(ExtensionItemAutoTaggingExtensionName.AwsAutoTagging)] + public void SerializationRoundtrip_Works(ExtensionItemAutoTaggingExtensionName rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class ExtensionItemAIAutoDescriptionTest : TestBase +{ + [Fact] + public void DefaultValidation_Works() + { + var constant = new ExtensionItemAIAutoDescription(); + constant.Validate(); + } + + [Fact] + public void ValidConstantValidation_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.Deserialize( + """ + { + "name": "ai-auto-description" + } + """ + ), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + constant.Validate(); + } + + [Fact] + public void InvalidConstantValidationThrows_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + Assert.Throws(() => constant.Validate()); + } + + [Fact] + public void DefaultRoundtrip_Works() + { + var constant = new ExtensionItemAIAutoDescription(); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void ValidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.Deserialize( + """ + { + "name": "ai-auto-description" + } + """ + ), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void InvalidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } +} + +public class ExtensionItemAITasksTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExtensionItemAITasks + { + Tasks = + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + ], + }; + + JsonElement expectedName = JsonSerializer.SerializeToElement("ai-tasks"); + List expectedTasks = + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + ]; + + Assert.True(JsonElement.DeepEquals(expectedName, model.Name)); + Assert.Equal(expectedTasks.Count, model.Tasks.Count); + for (int i = 0; i < expectedTasks.Count; i++) + { + Assert.Equal(expectedTasks[i], model.Tasks[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExtensionItemAITasks + { + Tasks = + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + ], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExtensionItemAITasks + { + Tasks = + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + ], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + JsonElement expectedName = JsonSerializer.SerializeToElement("ai-tasks"); + List expectedTasks = + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + ]; + + Assert.True(JsonElement.DeepEquals(expectedName, deserialized.Name)); + Assert.Equal(expectedTasks.Count, deserialized.Tasks.Count); + for (int i = 0; i < expectedTasks.Count; i++) + { + Assert.Equal(expectedTasks[i], deserialized.Tasks[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new ExtensionItemAITasks + { + Tasks = + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + ], + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExtensionItemAITasks + { + Tasks = + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + ], + }; + + ExtensionItemAITasks copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExtensionItemAITasksTaskTest : TestBase +{ + [Fact] + public void SelectTagsValidationWorks() + { + ExtensionItemAITasksTask value = new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }; + value.Validate(); + } + + [Fact] + public void SelectMetadataValidationWorks() + { + ExtensionItemAITasksTask value = new ExtensionItemAITasksTaskSelectMetadata() + { + Field = "primary_color", + Instruction = "What is the primary color of the clothing?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["red", "blue", "green", "black", "white"], + }; + value.Validate(); + } + + [Fact] + public void YesNoValidationWorks() + { + ExtensionItemAITasksTask value = new ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }; + value.Validate(); + } + + [Fact] + public void SelectTagsSerializationRoundtripWorks() + { + ExtensionItemAITasksTask value = new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void SelectMetadataSerializationRoundtripWorks() + { + ExtensionItemAITasksTask value = new ExtensionItemAITasksTaskSelectMetadata() + { + Field = "primary_color", + Instruction = "What is the primary color of the clothing?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["red", "blue", "green", "black", "white"], + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void YesNoSerializationRoundtripWorks() + { + ExtensionItemAITasksTask value = new ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class ExtensionItemAITasksTaskSelectTagsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskSelectTags + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }; + + string expectedInstruction = "What types of clothing items are visible in this image?"; + JsonElement expectedType = JsonSerializer.SerializeToElement("select_tags"); + long expectedMaxSelections = 1; + long expectedMinSelections = 0; + List expectedVocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"]; + + Assert.Equal(expectedInstruction, model.Instruction); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedMaxSelections, model.MaxSelections); + Assert.Equal(expectedMinSelections, model.MinSelections); + Assert.NotNull(model.Vocabulary); + Assert.Equal(expectedVocabulary.Count, model.Vocabulary.Count); + for (int i = 0; i < expectedVocabulary.Count; i++) + { + Assert.Equal(expectedVocabulary[i], model.Vocabulary[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskSelectTags + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExtensionItemAITasksTaskSelectTags + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedInstruction = "What types of clothing items are visible in this image?"; + JsonElement expectedType = JsonSerializer.SerializeToElement("select_tags"); + long expectedMaxSelections = 1; + long expectedMinSelections = 0; + List expectedVocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"]; + + Assert.Equal(expectedInstruction, deserialized.Instruction); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedMaxSelections, deserialized.MaxSelections); + Assert.Equal(expectedMinSelections, deserialized.MinSelections); + Assert.NotNull(deserialized.Vocabulary); + Assert.Equal(expectedVocabulary.Count, deserialized.Vocabulary.Count); + for (int i = 0; i < expectedVocabulary.Count; i++) + { + Assert.Equal(expectedVocabulary[i], deserialized.Vocabulary[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new ExtensionItemAITasksTaskSelectTags + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ExtensionItemAITasksTaskSelectTags + { + Instruction = "What types of clothing items are visible in this image?", + }; + + Assert.Null(model.MaxSelections); + Assert.False(model.RawData.ContainsKey("max_selections")); + Assert.Null(model.MinSelections); + Assert.False(model.RawData.ContainsKey("min_selections")); + Assert.Null(model.Vocabulary); + Assert.False(model.RawData.ContainsKey("vocabulary")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new ExtensionItemAITasksTaskSelectTags + { + Instruction = "What types of clothing items are visible in this image?", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ExtensionItemAITasksTaskSelectTags + { + Instruction = "What types of clothing items are visible in this image?", + + // Null should be interpreted as omitted for these properties + MaxSelections = null, + MinSelections = null, + Vocabulary = null, + }; + + Assert.Null(model.MaxSelections); + Assert.False(model.RawData.ContainsKey("max_selections")); + Assert.Null(model.MinSelections); + Assert.False(model.RawData.ContainsKey("min_selections")); + Assert.Null(model.Vocabulary); + Assert.False(model.RawData.ContainsKey("vocabulary")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new ExtensionItemAITasksTaskSelectTags + { + Instruction = "What types of clothing items are visible in this image?", + + // Null should be interpreted as omitted for these properties + MaxSelections = null, + MinSelections = null, + Vocabulary = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExtensionItemAITasksTaskSelectTags + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }; + + ExtensionItemAITasksTaskSelectTags copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExtensionItemAITasksTaskSelectMetadataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskSelectMetadata + { + Field = "primary_color", + Instruction = "What is the primary color of the clothing?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["red", "blue", "green", "black", "white"], + }; + + string expectedField = "primary_color"; + string expectedInstruction = "What is the primary color of the clothing?"; + JsonElement expectedType = JsonSerializer.SerializeToElement("select_metadata"); + long expectedMaxSelections = 1; + long expectedMinSelections = 0; + List expectedVocabulary = + [ + "red", + "blue", + "green", + "black", + "white", + ]; + + Assert.Equal(expectedField, model.Field); + Assert.Equal(expectedInstruction, model.Instruction); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedMaxSelections, model.MaxSelections); + Assert.Equal(expectedMinSelections, model.MinSelections); + Assert.NotNull(model.Vocabulary); + Assert.Equal(expectedVocabulary.Count, model.Vocabulary.Count); + for (int i = 0; i < expectedVocabulary.Count; i++) + { + Assert.Equal(expectedVocabulary[i], model.Vocabulary[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskSelectMetadata + { + Field = "primary_color", + Instruction = "What is the primary color of the clothing?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["red", "blue", "green", "black", "white"], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExtensionItemAITasksTaskSelectMetadata + { + Field = "primary_color", + Instruction = "What is the primary color of the clothing?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["red", "blue", "green", "black", "white"], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedField = "primary_color"; + string expectedInstruction = "What is the primary color of the clothing?"; + JsonElement expectedType = JsonSerializer.SerializeToElement("select_metadata"); + long expectedMaxSelections = 1; + long expectedMinSelections = 0; + List expectedVocabulary = + [ + "red", + "blue", + "green", + "black", + "white", + ]; + + Assert.Equal(expectedField, deserialized.Field); + Assert.Equal(expectedInstruction, deserialized.Instruction); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedMaxSelections, deserialized.MaxSelections); + Assert.Equal(expectedMinSelections, deserialized.MinSelections); + Assert.NotNull(deserialized.Vocabulary); + Assert.Equal(expectedVocabulary.Count, deserialized.Vocabulary.Count); + for (int i = 0; i < expectedVocabulary.Count; i++) + { + Assert.Equal(expectedVocabulary[i], deserialized.Vocabulary[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new ExtensionItemAITasksTaskSelectMetadata + { + Field = "primary_color", + Instruction = "What is the primary color of the clothing?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["red", "blue", "green", "black", "white"], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ExtensionItemAITasksTaskSelectMetadata + { + Field = "primary_color", + Instruction = "What is the primary color of the clothing?", + }; + + Assert.Null(model.MaxSelections); + Assert.False(model.RawData.ContainsKey("max_selections")); + Assert.Null(model.MinSelections); + Assert.False(model.RawData.ContainsKey("min_selections")); + Assert.Null(model.Vocabulary); + Assert.False(model.RawData.ContainsKey("vocabulary")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new ExtensionItemAITasksTaskSelectMetadata + { + Field = "primary_color", + Instruction = "What is the primary color of the clothing?", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ExtensionItemAITasksTaskSelectMetadata + { + Field = "primary_color", + Instruction = "What is the primary color of the clothing?", + + // Null should be interpreted as omitted for these properties + MaxSelections = null, + MinSelections = null, + Vocabulary = null, + }; + + Assert.Null(model.MaxSelections); + Assert.False(model.RawData.ContainsKey("max_selections")); + Assert.Null(model.MinSelections); + Assert.False(model.RawData.ContainsKey("min_selections")); + Assert.Null(model.Vocabulary); + Assert.False(model.RawData.ContainsKey("vocabulary")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new ExtensionItemAITasksTaskSelectMetadata + { + Field = "primary_color", + Instruction = "What is the primary color of the clothing?", + + // Null should be interpreted as omitted for these properties + MaxSelections = null, + MinSelections = null, + Vocabulary = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExtensionItemAITasksTaskSelectMetadata + { + Field = "primary_color", + Instruction = "What is the primary color of the clothing?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["red", "blue", "green", "black", "white"], + }; + + ExtensionItemAITasksTaskSelectMetadata copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExtensionItemAITasksTaskSelectMetadataVocabularyTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + ExtensionItemAITasksTaskSelectMetadataVocabulary value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + ExtensionItemAITasksTaskSelectMetadataVocabulary value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + ExtensionItemAITasksTaskSelectMetadataVocabulary value = true; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskSelectMetadataVocabulary value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskSelectMetadataVocabulary value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskSelectMetadataVocabulary value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class ExtensionItemAITasksTaskYesNoTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskYesNo + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }; + + string expectedInstruction = "Is this a luxury or high-end fashion item?"; + JsonElement expectedType = JsonSerializer.SerializeToElement("yes_no"); + ExtensionItemAITasksTaskYesNoOnNo expectedOnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + ExtensionItemAITasksTaskYesNoOnUnknown expectedOnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + ExtensionItemAITasksTaskYesNoOnYes expectedOnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + Assert.Equal(expectedInstruction, model.Instruction); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedOnNo, model.OnNo); + Assert.Equal(expectedOnUnknown, model.OnUnknown); + Assert.Equal(expectedOnYes, model.OnYes); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskYesNo + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExtensionItemAITasksTaskYesNo + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedInstruction = "Is this a luxury or high-end fashion item?"; + JsonElement expectedType = JsonSerializer.SerializeToElement("yes_no"); + ExtensionItemAITasksTaskYesNoOnNo expectedOnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + ExtensionItemAITasksTaskYesNoOnUnknown expectedOnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + ExtensionItemAITasksTaskYesNoOnYes expectedOnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + Assert.Equal(expectedInstruction, deserialized.Instruction); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedOnNo, deserialized.OnNo); + Assert.Equal(expectedOnUnknown, deserialized.OnUnknown); + Assert.Equal(expectedOnYes, deserialized.OnYes); + } + + [Fact] + public void Validation_Works() + { + var model = new ExtensionItemAITasksTaskYesNo + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ExtensionItemAITasksTaskYesNo + { + Instruction = "Is this a luxury or high-end fashion item?", + }; + + Assert.Null(model.OnNo); + Assert.False(model.RawData.ContainsKey("on_no")); + Assert.Null(model.OnUnknown); + Assert.False(model.RawData.ContainsKey("on_unknown")); + Assert.Null(model.OnYes); + Assert.False(model.RawData.ContainsKey("on_yes")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new ExtensionItemAITasksTaskYesNo + { + Instruction = "Is this a luxury or high-end fashion item?", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ExtensionItemAITasksTaskYesNo + { + Instruction = "Is this a luxury or high-end fashion item?", + + // Null should be interpreted as omitted for these properties + OnNo = null, + OnUnknown = null, + OnYes = null, + }; + + Assert.Null(model.OnNo); + Assert.False(model.RawData.ContainsKey("on_no")); + Assert.Null(model.OnUnknown); + Assert.False(model.RawData.ContainsKey("on_unknown")); + Assert.Null(model.OnYes); + Assert.False(model.RawData.ContainsKey("on_yes")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new ExtensionItemAITasksTaskYesNo + { + Instruction = "Is this a luxury or high-end fashion item?", + + // Null should be interpreted as omitted for these properties + OnNo = null, + OnUnknown = null, + OnYes = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExtensionItemAITasksTaskYesNo + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }; + + ExtensionItemAITasksTaskYesNo copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExtensionItemAITasksTaskYesNoOnNoTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnNo + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + List expectedAddTags = ["luxury", "premium"]; + List expectedRemoveTags = ["budget", "affordable"]; + List expectedSetMetadata = + [ + new() { Field = "price_range", Value = "premium" }, + ]; + List expectedUnsetMetadata = + [ + new("price_range"), + ]; + + Assert.NotNull(model.AddTags); + Assert.Equal(expectedAddTags.Count, model.AddTags.Count); + for (int i = 0; i < expectedAddTags.Count; i++) + { + Assert.Equal(expectedAddTags[i], model.AddTags[i]); + } + Assert.NotNull(model.RemoveTags); + Assert.Equal(expectedRemoveTags.Count, model.RemoveTags.Count); + for (int i = 0; i < expectedRemoveTags.Count; i++) + { + Assert.Equal(expectedRemoveTags[i], model.RemoveTags[i]); + } + Assert.NotNull(model.SetMetadata); + Assert.Equal(expectedSetMetadata.Count, model.SetMetadata.Count); + for (int i = 0; i < expectedSetMetadata.Count; i++) + { + Assert.Equal(expectedSetMetadata[i], model.SetMetadata[i]); + } + Assert.NotNull(model.UnsetMetadata); + Assert.Equal(expectedUnsetMetadata.Count, model.UnsetMetadata.Count); + for (int i = 0; i < expectedUnsetMetadata.Count; i++) + { + Assert.Equal(expectedUnsetMetadata[i], model.UnsetMetadata[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnNo + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnNo + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + List expectedAddTags = ["luxury", "premium"]; + List expectedRemoveTags = ["budget", "affordable"]; + List expectedSetMetadata = + [ + new() { Field = "price_range", Value = "premium" }, + ]; + List expectedUnsetMetadata = + [ + new("price_range"), + ]; + + Assert.NotNull(deserialized.AddTags); + Assert.Equal(expectedAddTags.Count, deserialized.AddTags.Count); + for (int i = 0; i < expectedAddTags.Count; i++) + { + Assert.Equal(expectedAddTags[i], deserialized.AddTags[i]); + } + Assert.NotNull(deserialized.RemoveTags); + Assert.Equal(expectedRemoveTags.Count, deserialized.RemoveTags.Count); + for (int i = 0; i < expectedRemoveTags.Count; i++) + { + Assert.Equal(expectedRemoveTags[i], deserialized.RemoveTags[i]); + } + Assert.NotNull(deserialized.SetMetadata); + Assert.Equal(expectedSetMetadata.Count, deserialized.SetMetadata.Count); + for (int i = 0; i < expectedSetMetadata.Count; i++) + { + Assert.Equal(expectedSetMetadata[i], deserialized.SetMetadata[i]); + } + Assert.NotNull(deserialized.UnsetMetadata); + Assert.Equal(expectedUnsetMetadata.Count, deserialized.UnsetMetadata.Count); + for (int i = 0; i < expectedUnsetMetadata.Count; i++) + { + Assert.Equal(expectedUnsetMetadata[i], deserialized.UnsetMetadata[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnNo + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnNo { }; + + Assert.Null(model.AddTags); + Assert.False(model.RawData.ContainsKey("add_tags")); + Assert.Null(model.RemoveTags); + Assert.False(model.RawData.ContainsKey("remove_tags")); + Assert.Null(model.SetMetadata); + Assert.False(model.RawData.ContainsKey("set_metadata")); + Assert.Null(model.UnsetMetadata); + Assert.False(model.RawData.ContainsKey("unset_metadata")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnNo { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnNo + { + // Null should be interpreted as omitted for these properties + AddTags = null, + RemoveTags = null, + SetMetadata = null, + UnsetMetadata = null, + }; + + Assert.Null(model.AddTags); + Assert.False(model.RawData.ContainsKey("add_tags")); + Assert.Null(model.RemoveTags); + Assert.False(model.RawData.ContainsKey("remove_tags")); + Assert.Null(model.SetMetadata); + Assert.False(model.RawData.ContainsKey("set_metadata")); + Assert.Null(model.UnsetMetadata); + Assert.False(model.RawData.ContainsKey("unset_metadata")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnNo + { + // Null should be interpreted as omitted for these properties + AddTags = null, + RemoveTags = null, + SetMetadata = null, + UnsetMetadata = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnNo + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + ExtensionItemAITasksTaskYesNoOnNo copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExtensionItemAITasksTaskYesNoOnNoSetMetadataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnNoSetMetadata + { + Field = "field", + Value = "string", + }; + + string expectedField = "field"; + ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue expectedValue = "string"; + + Assert.Equal(expectedField, model.Field); + Assert.Equal(expectedValue, model.Value); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnNoSetMetadata + { + Field = "field", + Value = "string", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnNoSetMetadata + { + Field = "field", + Value = "string", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedField = "field"; + ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue expectedValue = "string"; + + Assert.Equal(expectedField, deserialized.Field); + Assert.Equal(expectedValue, deserialized.Value); + } + + [Fact] + public void Validation_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnNoSetMetadata + { + Field = "field", + Value = "string", + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnNoSetMetadata + { + Field = "field", + Value = "string", + }; + + ExtensionItemAITasksTaskYesNoOnNoSetMetadata copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue value = true; + value.Validate(); + } + + [Fact] + public void MixedValidationWorks() + { + ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue value = new( + [new ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem("string")] + ); + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void MixedSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue value = new( + [new ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem("string")] + ); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItemTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem value = true; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class ExtensionItemAITasksTaskYesNoOnNoUnsetMetadataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnNoUnsetMetadata { Field = "field" }; + + string expectedField = "field"; + + Assert.Equal(expectedField, model.Field); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnNoUnsetMetadata { Field = "field" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnNoUnsetMetadata { Field = "field" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedField = "field"; + + Assert.Equal(expectedField, deserialized.Field); + } + + [Fact] + public void Validation_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnNoUnsetMetadata { Field = "field" }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnNoUnsetMetadata { Field = "field" }; + + ExtensionItemAITasksTaskYesNoOnNoUnsetMetadata copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExtensionItemAITasksTaskYesNoOnUnknownTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnUnknown + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + List expectedAddTags = ["luxury", "premium"]; + List expectedRemoveTags = ["budget", "affordable"]; + List expectedSetMetadata = + [ + new() { Field = "price_range", Value = "premium" }, + ]; + List expectedUnsetMetadata = + [ + new("price_range"), + ]; + + Assert.NotNull(model.AddTags); + Assert.Equal(expectedAddTags.Count, model.AddTags.Count); + for (int i = 0; i < expectedAddTags.Count; i++) + { + Assert.Equal(expectedAddTags[i], model.AddTags[i]); + } + Assert.NotNull(model.RemoveTags); + Assert.Equal(expectedRemoveTags.Count, model.RemoveTags.Count); + for (int i = 0; i < expectedRemoveTags.Count; i++) + { + Assert.Equal(expectedRemoveTags[i], model.RemoveTags[i]); + } + Assert.NotNull(model.SetMetadata); + Assert.Equal(expectedSetMetadata.Count, model.SetMetadata.Count); + for (int i = 0; i < expectedSetMetadata.Count; i++) + { + Assert.Equal(expectedSetMetadata[i], model.SetMetadata[i]); + } + Assert.NotNull(model.UnsetMetadata); + Assert.Equal(expectedUnsetMetadata.Count, model.UnsetMetadata.Count); + for (int i = 0; i < expectedUnsetMetadata.Count; i++) + { + Assert.Equal(expectedUnsetMetadata[i], model.UnsetMetadata[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnUnknown + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnUnknown + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + List expectedAddTags = ["luxury", "premium"]; + List expectedRemoveTags = ["budget", "affordable"]; + List expectedSetMetadata = + [ + new() { Field = "price_range", Value = "premium" }, + ]; + List expectedUnsetMetadata = + [ + new("price_range"), + ]; + + Assert.NotNull(deserialized.AddTags); + Assert.Equal(expectedAddTags.Count, deserialized.AddTags.Count); + for (int i = 0; i < expectedAddTags.Count; i++) + { + Assert.Equal(expectedAddTags[i], deserialized.AddTags[i]); + } + Assert.NotNull(deserialized.RemoveTags); + Assert.Equal(expectedRemoveTags.Count, deserialized.RemoveTags.Count); + for (int i = 0; i < expectedRemoveTags.Count; i++) + { + Assert.Equal(expectedRemoveTags[i], deserialized.RemoveTags[i]); + } + Assert.NotNull(deserialized.SetMetadata); + Assert.Equal(expectedSetMetadata.Count, deserialized.SetMetadata.Count); + for (int i = 0; i < expectedSetMetadata.Count; i++) + { + Assert.Equal(expectedSetMetadata[i], deserialized.SetMetadata[i]); + } + Assert.NotNull(deserialized.UnsetMetadata); + Assert.Equal(expectedUnsetMetadata.Count, deserialized.UnsetMetadata.Count); + for (int i = 0; i < expectedUnsetMetadata.Count; i++) + { + Assert.Equal(expectedUnsetMetadata[i], deserialized.UnsetMetadata[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnUnknown + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnUnknown { }; + + Assert.Null(model.AddTags); + Assert.False(model.RawData.ContainsKey("add_tags")); + Assert.Null(model.RemoveTags); + Assert.False(model.RawData.ContainsKey("remove_tags")); + Assert.Null(model.SetMetadata); + Assert.False(model.RawData.ContainsKey("set_metadata")); + Assert.Null(model.UnsetMetadata); + Assert.False(model.RawData.ContainsKey("unset_metadata")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnUnknown { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnUnknown + { + // Null should be interpreted as omitted for these properties + AddTags = null, + RemoveTags = null, + SetMetadata = null, + UnsetMetadata = null, + }; + + Assert.Null(model.AddTags); + Assert.False(model.RawData.ContainsKey("add_tags")); + Assert.Null(model.RemoveTags); + Assert.False(model.RawData.ContainsKey("remove_tags")); + Assert.Null(model.SetMetadata); + Assert.False(model.RawData.ContainsKey("set_metadata")); + Assert.Null(model.UnsetMetadata); + Assert.False(model.RawData.ContainsKey("unset_metadata")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnUnknown + { + // Null should be interpreted as omitted for these properties + AddTags = null, + RemoveTags = null, + SetMetadata = null, + UnsetMetadata = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnUnknown + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + ExtensionItemAITasksTaskYesNoOnUnknown copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnUnknownSetMetadata + { + Field = "field", + Value = "string", + }; + + string expectedField = "field"; + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue expectedValue = "string"; + + Assert.Equal(expectedField, model.Field); + Assert.Equal(expectedValue, model.Value); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnUnknownSetMetadata + { + Field = "field", + Value = "string", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnUnknownSetMetadata + { + Field = "field", + Value = "string", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedField = "field"; + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue expectedValue = "string"; + + Assert.Equal(expectedField, deserialized.Field); + Assert.Equal(expectedValue, deserialized.Value); + } + + [Fact] + public void Validation_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnUnknownSetMetadata + { + Field = "field", + Value = "string", + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnUnknownSetMetadata + { + Field = "field", + Value = "string", + }; + + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadata copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue value = true; + value.Validate(); + } + + [Fact] + public void MixedValidationWorks() + { + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue value = new( + [new ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem("string")] + ); + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void MixedSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue value = new( + [new ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem("string")] + ); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItemTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem value = true; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class ExtensionItemAITasksTaskYesNoOnUnknownUnsetMetadataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnUnknownUnsetMetadata { Field = "field" }; + + string expectedField = "field"; + + Assert.Equal(expectedField, model.Field); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnUnknownUnsetMetadata { Field = "field" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnUnknownUnsetMetadata { Field = "field" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedField = "field"; + + Assert.Equal(expectedField, deserialized.Field); + } + + [Fact] + public void Validation_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnUnknownUnsetMetadata { Field = "field" }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnUnknownUnsetMetadata { Field = "field" }; + + ExtensionItemAITasksTaskYesNoOnUnknownUnsetMetadata copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExtensionItemAITasksTaskYesNoOnYesTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnYes + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + List expectedAddTags = ["luxury", "premium"]; + List expectedRemoveTags = ["budget", "affordable"]; + List expectedSetMetadata = + [ + new() { Field = "price_range", Value = "premium" }, + ]; + List expectedUnsetMetadata = + [ + new("price_range"), + ]; + + Assert.NotNull(model.AddTags); + Assert.Equal(expectedAddTags.Count, model.AddTags.Count); + for (int i = 0; i < expectedAddTags.Count; i++) + { + Assert.Equal(expectedAddTags[i], model.AddTags[i]); + } + Assert.NotNull(model.RemoveTags); + Assert.Equal(expectedRemoveTags.Count, model.RemoveTags.Count); + for (int i = 0; i < expectedRemoveTags.Count; i++) + { + Assert.Equal(expectedRemoveTags[i], model.RemoveTags[i]); + } + Assert.NotNull(model.SetMetadata); + Assert.Equal(expectedSetMetadata.Count, model.SetMetadata.Count); + for (int i = 0; i < expectedSetMetadata.Count; i++) + { + Assert.Equal(expectedSetMetadata[i], model.SetMetadata[i]); + } + Assert.NotNull(model.UnsetMetadata); + Assert.Equal(expectedUnsetMetadata.Count, model.UnsetMetadata.Count); + for (int i = 0; i < expectedUnsetMetadata.Count; i++) + { + Assert.Equal(expectedUnsetMetadata[i], model.UnsetMetadata[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnYes + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnYes + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + List expectedAddTags = ["luxury", "premium"]; + List expectedRemoveTags = ["budget", "affordable"]; + List expectedSetMetadata = + [ + new() { Field = "price_range", Value = "premium" }, + ]; + List expectedUnsetMetadata = + [ + new("price_range"), + ]; + + Assert.NotNull(deserialized.AddTags); + Assert.Equal(expectedAddTags.Count, deserialized.AddTags.Count); + for (int i = 0; i < expectedAddTags.Count; i++) + { + Assert.Equal(expectedAddTags[i], deserialized.AddTags[i]); + } + Assert.NotNull(deserialized.RemoveTags); + Assert.Equal(expectedRemoveTags.Count, deserialized.RemoveTags.Count); + for (int i = 0; i < expectedRemoveTags.Count; i++) + { + Assert.Equal(expectedRemoveTags[i], deserialized.RemoveTags[i]); + } + Assert.NotNull(deserialized.SetMetadata); + Assert.Equal(expectedSetMetadata.Count, deserialized.SetMetadata.Count); + for (int i = 0; i < expectedSetMetadata.Count; i++) + { + Assert.Equal(expectedSetMetadata[i], deserialized.SetMetadata[i]); + } + Assert.NotNull(deserialized.UnsetMetadata); + Assert.Equal(expectedUnsetMetadata.Count, deserialized.UnsetMetadata.Count); + for (int i = 0; i < expectedUnsetMetadata.Count; i++) + { + Assert.Equal(expectedUnsetMetadata[i], deserialized.UnsetMetadata[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnYes + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnYes { }; + + Assert.Null(model.AddTags); + Assert.False(model.RawData.ContainsKey("add_tags")); + Assert.Null(model.RemoveTags); + Assert.False(model.RawData.ContainsKey("remove_tags")); + Assert.Null(model.SetMetadata); + Assert.False(model.RawData.ContainsKey("set_metadata")); + Assert.Null(model.UnsetMetadata); + Assert.False(model.RawData.ContainsKey("unset_metadata")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnYes { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnYes + { + // Null should be interpreted as omitted for these properties + AddTags = null, + RemoveTags = null, + SetMetadata = null, + UnsetMetadata = null, + }; + + Assert.Null(model.AddTags); + Assert.False(model.RawData.ContainsKey("add_tags")); + Assert.Null(model.RemoveTags); + Assert.False(model.RawData.ContainsKey("remove_tags")); + Assert.Null(model.SetMetadata); + Assert.False(model.RawData.ContainsKey("set_metadata")); + Assert.Null(model.UnsetMetadata); + Assert.False(model.RawData.ContainsKey("unset_metadata")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnYes + { + // Null should be interpreted as omitted for these properties + AddTags = null, + RemoveTags = null, + SetMetadata = null, + UnsetMetadata = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnYes + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }; + + ExtensionItemAITasksTaskYesNoOnYes copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExtensionItemAITasksTaskYesNoOnYesSetMetadataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnYesSetMetadata + { + Field = "field", + Value = "string", + }; + + string expectedField = "field"; + ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue expectedValue = "string"; + + Assert.Equal(expectedField, model.Field); + Assert.Equal(expectedValue, model.Value); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnYesSetMetadata + { + Field = "field", + Value = "string", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnYesSetMetadata + { + Field = "field", + Value = "string", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedField = "field"; + ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue expectedValue = "string"; + + Assert.Equal(expectedField, deserialized.Field); + Assert.Equal(expectedValue, deserialized.Value); + } + + [Fact] + public void Validation_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnYesSetMetadata + { + Field = "field", + Value = "string", + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnYesSetMetadata + { + Field = "field", + Value = "string", + }; + + ExtensionItemAITasksTaskYesNoOnYesSetMetadata copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue value = true; + value.Validate(); + } + + [Fact] + public void MixedValidationWorks() + { + ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue value = new( + [new ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem("string")] + ); + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void MixedSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue value = new( + [new ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem("string")] + ); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItemTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem value = true; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class ExtensionItemAITasksTaskYesNoOnYesUnsetMetadataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnYesUnsetMetadata { Field = "field" }; + + string expectedField = "field"; + + Assert.Equal(expectedField, model.Field); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnYesUnsetMetadata { Field = "field" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnYesUnsetMetadata { Field = "field" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedField = "field"; + + Assert.Equal(expectedField, deserialized.Field); + } + + [Fact] + public void Validation_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnYesUnsetMetadata { Field = "field" }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExtensionItemAITasksTaskYesNoOnYesUnsetMetadata { Field = "field" }; + + ExtensionItemAITasksTaskYesNoOnYesUnsetMetadata copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class SavedExtensionTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SavedExtension { ID = "ext_abc123" }; + + string expectedID = "ext_abc123"; + JsonElement expectedName = JsonSerializer.SerializeToElement("saved-extension"); + + Assert.Equal(expectedID, model.ID); + Assert.True(JsonElement.DeepEquals(expectedName, model.Name)); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SavedExtension { ID = "ext_abc123" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SavedExtension { ID = "ext_abc123" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "ext_abc123"; + JsonElement expectedName = JsonSerializer.SerializeToElement("saved-extension"); + + Assert.Equal(expectedID, deserialized.ID); + Assert.True(JsonElement.DeepEquals(expectedName, deserialized.Name)); + } + + [Fact] + public void Validation_Works() + { + var model = new SavedExtension { ID = "ext_abc123" }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SavedExtension { ID = "ext_abc123" }; + + SavedExtension copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/Bulk/BulkAddTagsParamsTest.cs b/src/Imagekit.Tests/Models/Files/Bulk/BulkAddTagsParamsTest.cs new file mode 100644 index 00000000..360fea0c --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/Bulk/BulkAddTagsParamsTest.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using Imagekit.Models.Files.Bulk; + +namespace Imagekit.Tests.Models.Files.Bulk; + +public class BulkAddTagsParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new BulkAddTagsParams + { + FileIds = ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + Tags = ["t-shirt", "round-neck", "sale2019"], + }; + + List expectedFileIds = ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"]; + List expectedTags = ["t-shirt", "round-neck", "sale2019"]; + + Assert.Equal(expectedFileIds.Count, parameters.FileIds.Count); + for (int i = 0; i < expectedFileIds.Count; i++) + { + Assert.Equal(expectedFileIds[i], parameters.FileIds[i]); + } + Assert.Equal(expectedTags.Count, parameters.Tags.Count); + for (int i = 0; i < expectedTags.Count; i++) + { + Assert.Equal(expectedTags[i], parameters.Tags[i]); + } + } + + [Fact] + public void Url_Works() + { + BulkAddTagsParams parameters = new() + { + FileIds = ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + Tags = ["t-shirt", "round-neck", "sale2019"], + }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True(TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/files/addTags"), url)); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new BulkAddTagsParams + { + FileIds = ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + Tags = ["t-shirt", "round-neck", "sale2019"], + }; + + BulkAddTagsParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/Bulk/BulkAddTagsResponseTest.cs b/src/Imagekit.Tests/Models/Files/Bulk/BulkAddTagsResponseTest.cs new file mode 100644 index 00000000..493b50a2 --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/Bulk/BulkAddTagsResponseTest.cs @@ -0,0 +1,132 @@ +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Files.Bulk; + +namespace Imagekit.Tests.Models.Files.Bulk; + +public class BulkAddTagsResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new BulkAddTagsResponse { SuccessfullyUpdatedFileIds = ["string"] }; + + List expectedSuccessfullyUpdatedFileIds = ["string"]; + + Assert.NotNull(model.SuccessfullyUpdatedFileIds); + Assert.Equal( + expectedSuccessfullyUpdatedFileIds.Count, + model.SuccessfullyUpdatedFileIds.Count + ); + for (int i = 0; i < expectedSuccessfullyUpdatedFileIds.Count; i++) + { + Assert.Equal( + expectedSuccessfullyUpdatedFileIds[i], + model.SuccessfullyUpdatedFileIds[i] + ); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new BulkAddTagsResponse { SuccessfullyUpdatedFileIds = ["string"] }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new BulkAddTagsResponse { SuccessfullyUpdatedFileIds = ["string"] }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + List expectedSuccessfullyUpdatedFileIds = ["string"]; + + Assert.NotNull(deserialized.SuccessfullyUpdatedFileIds); + Assert.Equal( + expectedSuccessfullyUpdatedFileIds.Count, + deserialized.SuccessfullyUpdatedFileIds.Count + ); + for (int i = 0; i < expectedSuccessfullyUpdatedFileIds.Count; i++) + { + Assert.Equal( + expectedSuccessfullyUpdatedFileIds[i], + deserialized.SuccessfullyUpdatedFileIds[i] + ); + } + } + + [Fact] + public void Validation_Works() + { + var model = new BulkAddTagsResponse { SuccessfullyUpdatedFileIds = ["string"] }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new BulkAddTagsResponse { }; + + Assert.Null(model.SuccessfullyUpdatedFileIds); + Assert.False(model.RawData.ContainsKey("successfullyUpdatedFileIds")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new BulkAddTagsResponse { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new BulkAddTagsResponse + { + // Null should be interpreted as omitted for these properties + SuccessfullyUpdatedFileIds = null, + }; + + Assert.Null(model.SuccessfullyUpdatedFileIds); + Assert.False(model.RawData.ContainsKey("successfullyUpdatedFileIds")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new BulkAddTagsResponse + { + // Null should be interpreted as omitted for these properties + SuccessfullyUpdatedFileIds = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new BulkAddTagsResponse { SuccessfullyUpdatedFileIds = ["string"] }; + + BulkAddTagsResponse copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/Bulk/BulkDeleteParamsTest.cs b/src/Imagekit.Tests/Models/Files/Bulk/BulkDeleteParamsTest.cs new file mode 100644 index 00000000..a06cf8ad --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/Bulk/BulkDeleteParamsTest.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using Imagekit.Models.Files.Bulk; + +namespace Imagekit.Tests.Models.Files.Bulk; + +public class BulkDeleteParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new BulkDeleteParams + { + FileIds = ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + }; + + List expectedFileIds = ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"]; + + Assert.Equal(expectedFileIds.Count, parameters.FileIds.Count); + for (int i = 0; i < expectedFileIds.Count; i++) + { + Assert.Equal(expectedFileIds[i], parameters.FileIds[i]); + } + } + + [Fact] + public void Url_Works() + { + BulkDeleteParams parameters = new() + { + FileIds = ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual( + new Uri("https://api.imagekit.io/v1/files/batch/deleteByFileIds"), + url + ) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new BulkDeleteParams + { + FileIds = ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + }; + + BulkDeleteParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/Bulk/BulkDeleteResponseTest.cs b/src/Imagekit.Tests/Models/Files/Bulk/BulkDeleteResponseTest.cs new file mode 100644 index 00000000..710c28aa --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/Bulk/BulkDeleteResponseTest.cs @@ -0,0 +1,132 @@ +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Files.Bulk; + +namespace Imagekit.Tests.Models.Files.Bulk; + +public class BulkDeleteResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new BulkDeleteResponse { SuccessfullyDeletedFileIds = ["string"] }; + + List expectedSuccessfullyDeletedFileIds = ["string"]; + + Assert.NotNull(model.SuccessfullyDeletedFileIds); + Assert.Equal( + expectedSuccessfullyDeletedFileIds.Count, + model.SuccessfullyDeletedFileIds.Count + ); + for (int i = 0; i < expectedSuccessfullyDeletedFileIds.Count; i++) + { + Assert.Equal( + expectedSuccessfullyDeletedFileIds[i], + model.SuccessfullyDeletedFileIds[i] + ); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new BulkDeleteResponse { SuccessfullyDeletedFileIds = ["string"] }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new BulkDeleteResponse { SuccessfullyDeletedFileIds = ["string"] }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + List expectedSuccessfullyDeletedFileIds = ["string"]; + + Assert.NotNull(deserialized.SuccessfullyDeletedFileIds); + Assert.Equal( + expectedSuccessfullyDeletedFileIds.Count, + deserialized.SuccessfullyDeletedFileIds.Count + ); + for (int i = 0; i < expectedSuccessfullyDeletedFileIds.Count; i++) + { + Assert.Equal( + expectedSuccessfullyDeletedFileIds[i], + deserialized.SuccessfullyDeletedFileIds[i] + ); + } + } + + [Fact] + public void Validation_Works() + { + var model = new BulkDeleteResponse { SuccessfullyDeletedFileIds = ["string"] }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new BulkDeleteResponse { }; + + Assert.Null(model.SuccessfullyDeletedFileIds); + Assert.False(model.RawData.ContainsKey("successfullyDeletedFileIds")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new BulkDeleteResponse { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new BulkDeleteResponse + { + // Null should be interpreted as omitted for these properties + SuccessfullyDeletedFileIds = null, + }; + + Assert.Null(model.SuccessfullyDeletedFileIds); + Assert.False(model.RawData.ContainsKey("successfullyDeletedFileIds")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new BulkDeleteResponse + { + // Null should be interpreted as omitted for these properties + SuccessfullyDeletedFileIds = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new BulkDeleteResponse { SuccessfullyDeletedFileIds = ["string"] }; + + BulkDeleteResponse copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/Bulk/BulkRemoveAITagsParamsTest.cs b/src/Imagekit.Tests/Models/Files/Bulk/BulkRemoveAITagsParamsTest.cs new file mode 100644 index 00000000..ec8fcfd8 --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/Bulk/BulkRemoveAITagsParamsTest.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using Imagekit.Models.Files.Bulk; + +namespace Imagekit.Tests.Models.Files.Bulk; + +public class BulkRemoveAITagsParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new BulkRemoveAITagsParams + { + AITags = ["t-shirt", "round-neck", "sale2019"], + FileIds = ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + }; + + List expectedAITags = ["t-shirt", "round-neck", "sale2019"]; + List expectedFileIds = ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"]; + + Assert.Equal(expectedAITags.Count, parameters.AITags.Count); + for (int i = 0; i < expectedAITags.Count; i++) + { + Assert.Equal(expectedAITags[i], parameters.AITags[i]); + } + Assert.Equal(expectedFileIds.Count, parameters.FileIds.Count); + for (int i = 0; i < expectedFileIds.Count; i++) + { + Assert.Equal(expectedFileIds[i], parameters.FileIds[i]); + } + } + + [Fact] + public void Url_Works() + { + BulkRemoveAITagsParams parameters = new() + { + AITags = ["t-shirt", "round-neck", "sale2019"], + FileIds = ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/files/removeAITags"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new BulkRemoveAITagsParams + { + AITags = ["t-shirt", "round-neck", "sale2019"], + FileIds = ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + }; + + BulkRemoveAITagsParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/Bulk/BulkRemoveAITagsResponseTest.cs b/src/Imagekit.Tests/Models/Files/Bulk/BulkRemoveAITagsResponseTest.cs new file mode 100644 index 00000000..ed0100fd --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/Bulk/BulkRemoveAITagsResponseTest.cs @@ -0,0 +1,132 @@ +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Files.Bulk; + +namespace Imagekit.Tests.Models.Files.Bulk; + +public class BulkRemoveAITagsResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new BulkRemoveAITagsResponse { SuccessfullyUpdatedFileIds = ["string"] }; + + List expectedSuccessfullyUpdatedFileIds = ["string"]; + + Assert.NotNull(model.SuccessfullyUpdatedFileIds); + Assert.Equal( + expectedSuccessfullyUpdatedFileIds.Count, + model.SuccessfullyUpdatedFileIds.Count + ); + for (int i = 0; i < expectedSuccessfullyUpdatedFileIds.Count; i++) + { + Assert.Equal( + expectedSuccessfullyUpdatedFileIds[i], + model.SuccessfullyUpdatedFileIds[i] + ); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new BulkRemoveAITagsResponse { SuccessfullyUpdatedFileIds = ["string"] }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new BulkRemoveAITagsResponse { SuccessfullyUpdatedFileIds = ["string"] }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + List expectedSuccessfullyUpdatedFileIds = ["string"]; + + Assert.NotNull(deserialized.SuccessfullyUpdatedFileIds); + Assert.Equal( + expectedSuccessfullyUpdatedFileIds.Count, + deserialized.SuccessfullyUpdatedFileIds.Count + ); + for (int i = 0; i < expectedSuccessfullyUpdatedFileIds.Count; i++) + { + Assert.Equal( + expectedSuccessfullyUpdatedFileIds[i], + deserialized.SuccessfullyUpdatedFileIds[i] + ); + } + } + + [Fact] + public void Validation_Works() + { + var model = new BulkRemoveAITagsResponse { SuccessfullyUpdatedFileIds = ["string"] }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new BulkRemoveAITagsResponse { }; + + Assert.Null(model.SuccessfullyUpdatedFileIds); + Assert.False(model.RawData.ContainsKey("successfullyUpdatedFileIds")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new BulkRemoveAITagsResponse { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new BulkRemoveAITagsResponse + { + // Null should be interpreted as omitted for these properties + SuccessfullyUpdatedFileIds = null, + }; + + Assert.Null(model.SuccessfullyUpdatedFileIds); + Assert.False(model.RawData.ContainsKey("successfullyUpdatedFileIds")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new BulkRemoveAITagsResponse + { + // Null should be interpreted as omitted for these properties + SuccessfullyUpdatedFileIds = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new BulkRemoveAITagsResponse { SuccessfullyUpdatedFileIds = ["string"] }; + + BulkRemoveAITagsResponse copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/Bulk/BulkRemoveTagsParamsTest.cs b/src/Imagekit.Tests/Models/Files/Bulk/BulkRemoveTagsParamsTest.cs new file mode 100644 index 00000000..78171036 --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/Bulk/BulkRemoveTagsParamsTest.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using Imagekit.Models.Files.Bulk; + +namespace Imagekit.Tests.Models.Files.Bulk; + +public class BulkRemoveTagsParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new BulkRemoveTagsParams + { + FileIds = ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + Tags = ["t-shirt", "round-neck", "sale2019"], + }; + + List expectedFileIds = ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"]; + List expectedTags = ["t-shirt", "round-neck", "sale2019"]; + + Assert.Equal(expectedFileIds.Count, parameters.FileIds.Count); + for (int i = 0; i < expectedFileIds.Count; i++) + { + Assert.Equal(expectedFileIds[i], parameters.FileIds[i]); + } + Assert.Equal(expectedTags.Count, parameters.Tags.Count); + for (int i = 0; i < expectedTags.Count; i++) + { + Assert.Equal(expectedTags[i], parameters.Tags[i]); + } + } + + [Fact] + public void Url_Works() + { + BulkRemoveTagsParams parameters = new() + { + FileIds = ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + Tags = ["t-shirt", "round-neck", "sale2019"], + }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/files/removeTags"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new BulkRemoveTagsParams + { + FileIds = ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + Tags = ["t-shirt", "round-neck", "sale2019"], + }; + + BulkRemoveTagsParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/Bulk/BulkRemoveTagsResponseTest.cs b/src/Imagekit.Tests/Models/Files/Bulk/BulkRemoveTagsResponseTest.cs new file mode 100644 index 00000000..6592db32 --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/Bulk/BulkRemoveTagsResponseTest.cs @@ -0,0 +1,132 @@ +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Files.Bulk; + +namespace Imagekit.Tests.Models.Files.Bulk; + +public class BulkRemoveTagsResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new BulkRemoveTagsResponse { SuccessfullyUpdatedFileIds = ["string"] }; + + List expectedSuccessfullyUpdatedFileIds = ["string"]; + + Assert.NotNull(model.SuccessfullyUpdatedFileIds); + Assert.Equal( + expectedSuccessfullyUpdatedFileIds.Count, + model.SuccessfullyUpdatedFileIds.Count + ); + for (int i = 0; i < expectedSuccessfullyUpdatedFileIds.Count; i++) + { + Assert.Equal( + expectedSuccessfullyUpdatedFileIds[i], + model.SuccessfullyUpdatedFileIds[i] + ); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new BulkRemoveTagsResponse { SuccessfullyUpdatedFileIds = ["string"] }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new BulkRemoveTagsResponse { SuccessfullyUpdatedFileIds = ["string"] }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + List expectedSuccessfullyUpdatedFileIds = ["string"]; + + Assert.NotNull(deserialized.SuccessfullyUpdatedFileIds); + Assert.Equal( + expectedSuccessfullyUpdatedFileIds.Count, + deserialized.SuccessfullyUpdatedFileIds.Count + ); + for (int i = 0; i < expectedSuccessfullyUpdatedFileIds.Count; i++) + { + Assert.Equal( + expectedSuccessfullyUpdatedFileIds[i], + deserialized.SuccessfullyUpdatedFileIds[i] + ); + } + } + + [Fact] + public void Validation_Works() + { + var model = new BulkRemoveTagsResponse { SuccessfullyUpdatedFileIds = ["string"] }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new BulkRemoveTagsResponse { }; + + Assert.Null(model.SuccessfullyUpdatedFileIds); + Assert.False(model.RawData.ContainsKey("successfullyUpdatedFileIds")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new BulkRemoveTagsResponse { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new BulkRemoveTagsResponse + { + // Null should be interpreted as omitted for these properties + SuccessfullyUpdatedFileIds = null, + }; + + Assert.Null(model.SuccessfullyUpdatedFileIds); + Assert.False(model.RawData.ContainsKey("successfullyUpdatedFileIds")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new BulkRemoveTagsResponse + { + // Null should be interpreted as omitted for these properties + SuccessfullyUpdatedFileIds = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new BulkRemoveTagsResponse { SuccessfullyUpdatedFileIds = ["string"] }; + + BulkRemoveTagsResponse copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/FileCopyParamsTest.cs b/src/Imagekit.Tests/Models/Files/FileCopyParamsTest.cs new file mode 100644 index 00000000..6bbfc391 --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/FileCopyParamsTest.cs @@ -0,0 +1,84 @@ +using System; +using Imagekit.Models.Files; + +namespace Imagekit.Tests.Models.Files; + +public class FileCopyParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new FileCopyParams + { + DestinationPath = "/folder/to/copy/into/", + SourceFilePath = "/path/to/file.jpg", + IncludeFileVersions = false, + }; + + string expectedDestinationPath = "/folder/to/copy/into/"; + string expectedSourceFilePath = "/path/to/file.jpg"; + bool expectedIncludeFileVersions = false; + + Assert.Equal(expectedDestinationPath, parameters.DestinationPath); + Assert.Equal(expectedSourceFilePath, parameters.SourceFilePath); + Assert.Equal(expectedIncludeFileVersions, parameters.IncludeFileVersions); + } + + [Fact] + public void OptionalNonNullableParamsUnsetAreNotSet_Works() + { + var parameters = new FileCopyParams + { + DestinationPath = "/folder/to/copy/into/", + SourceFilePath = "/path/to/file.jpg", + }; + + Assert.Null(parameters.IncludeFileVersions); + Assert.False(parameters.RawBodyData.ContainsKey("includeFileVersions")); + } + + [Fact] + public void OptionalNonNullableParamsSetToNullAreNotSet_Works() + { + var parameters = new FileCopyParams + { + DestinationPath = "/folder/to/copy/into/", + SourceFilePath = "/path/to/file.jpg", + + // Null should be interpreted as omitted for these properties + IncludeFileVersions = null, + }; + + Assert.Null(parameters.IncludeFileVersions); + Assert.False(parameters.RawBodyData.ContainsKey("includeFileVersions")); + } + + [Fact] + public void Url_Works() + { + FileCopyParams parameters = new() + { + DestinationPath = "/folder/to/copy/into/", + SourceFilePath = "/path/to/file.jpg", + }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True(TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/files/copy"), url)); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new FileCopyParams + { + DestinationPath = "/folder/to/copy/into/", + SourceFilePath = "/path/to/file.jpg", + IncludeFileVersions = false, + }; + + FileCopyParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/FileCopyResponseTest.cs b/src/Imagekit.Tests/Models/Files/FileCopyResponseTest.cs new file mode 100644 index 00000000..01cada79 --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/FileCopyResponseTest.cs @@ -0,0 +1,59 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Files; + +namespace Imagekit.Tests.Models.Files; + +public class FileCopyResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new FileCopyResponse { }; + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new FileCopyResponse { }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new FileCopyResponse { }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + } + + [Fact] + public void Validation_Works() + { + var model = new FileCopyResponse { }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new FileCopyResponse { }; + + FileCopyResponse copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/FileDeleteParamsTest.cs b/src/Imagekit.Tests/Models/Files/FileDeleteParamsTest.cs new file mode 100644 index 00000000..d01b71ec --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/FileDeleteParamsTest.cs @@ -0,0 +1,37 @@ +using System; +using Imagekit.Models.Files; + +namespace Imagekit.Tests.Models.Files; + +public class FileDeleteParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new FileDeleteParams { FileID = "fileId" }; + + string expectedFileID = "fileId"; + + Assert.Equal(expectedFileID, parameters.FileID); + } + + [Fact] + public void Url_Works() + { + FileDeleteParams parameters = new() { FileID = "fileId" }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True(TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/files/fileId"), url)); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new FileDeleteParams { FileID = "fileId" }; + + FileDeleteParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/FileGetParamsTest.cs b/src/Imagekit.Tests/Models/Files/FileGetParamsTest.cs new file mode 100644 index 00000000..0cea1d34 --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/FileGetParamsTest.cs @@ -0,0 +1,39 @@ +using System; +using Imagekit.Models.Files; + +namespace Imagekit.Tests.Models.Files; + +public class FileGetParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new FileGetParams { FileID = "fileId" }; + + string expectedFileID = "fileId"; + + Assert.Equal(expectedFileID, parameters.FileID); + } + + [Fact] + public void Url_Works() + { + FileGetParams parameters = new() { FileID = "fileId" }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/files/fileId/details"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new FileGetParams { FileID = "fileId" }; + + FileGetParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/FileMetadataTest.cs b/src/Imagekit.Tests/Models/Files/FileMetadataTest.cs new file mode 100644 index 00000000..a296b95d --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/FileMetadataTest.cs @@ -0,0 +1,2552 @@ +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Files; + +namespace Imagekit.Tests.Models.Files; + +public class FileMetadataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new FileMetadata + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }; + + string expectedAudioCodec = "audioCodec"; + long expectedBitRate = 0; + long expectedDensity = 0; + long expectedDuration = 0; + Exif expectedExif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }; + string expectedFormat = "format"; + bool expectedHasColorProfile = true; + bool expectedHasTransparency = true; + long expectedHeight = 0; + string expectedPHash = "pHash"; + long expectedQuality = 0; + long expectedSize = 0; + string expectedVideoCodec = "videoCodec"; + long expectedWidth = 0; + + Assert.Equal(expectedAudioCodec, model.AudioCodec); + Assert.Equal(expectedBitRate, model.BitRate); + Assert.Equal(expectedDensity, model.Density); + Assert.Equal(expectedDuration, model.Duration); + Assert.Equal(expectedExif, model.Exif); + Assert.Equal(expectedFormat, model.Format); + Assert.Equal(expectedHasColorProfile, model.HasColorProfile); + Assert.Equal(expectedHasTransparency, model.HasTransparency); + Assert.Equal(expectedHeight, model.Height); + Assert.Equal(expectedPHash, model.PHash); + Assert.Equal(expectedQuality, model.Quality); + Assert.Equal(expectedSize, model.Size); + Assert.Equal(expectedVideoCodec, model.VideoCodec); + Assert.Equal(expectedWidth, model.Width); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new FileMetadata + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new FileMetadata + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedAudioCodec = "audioCodec"; + long expectedBitRate = 0; + long expectedDensity = 0; + long expectedDuration = 0; + Exif expectedExif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }; + string expectedFormat = "format"; + bool expectedHasColorProfile = true; + bool expectedHasTransparency = true; + long expectedHeight = 0; + string expectedPHash = "pHash"; + long expectedQuality = 0; + long expectedSize = 0; + string expectedVideoCodec = "videoCodec"; + long expectedWidth = 0; + + Assert.Equal(expectedAudioCodec, deserialized.AudioCodec); + Assert.Equal(expectedBitRate, deserialized.BitRate); + Assert.Equal(expectedDensity, deserialized.Density); + Assert.Equal(expectedDuration, deserialized.Duration); + Assert.Equal(expectedExif, deserialized.Exif); + Assert.Equal(expectedFormat, deserialized.Format); + Assert.Equal(expectedHasColorProfile, deserialized.HasColorProfile); + Assert.Equal(expectedHasTransparency, deserialized.HasTransparency); + Assert.Equal(expectedHeight, deserialized.Height); + Assert.Equal(expectedPHash, deserialized.PHash); + Assert.Equal(expectedQuality, deserialized.Quality); + Assert.Equal(expectedSize, deserialized.Size); + Assert.Equal(expectedVideoCodec, deserialized.VideoCodec); + Assert.Equal(expectedWidth, deserialized.Width); + } + + [Fact] + public void Validation_Works() + { + var model = new FileMetadata + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new FileMetadata { }; + + Assert.Null(model.AudioCodec); + Assert.False(model.RawData.ContainsKey("audioCodec")); + Assert.Null(model.BitRate); + Assert.False(model.RawData.ContainsKey("bitRate")); + Assert.Null(model.Density); + Assert.False(model.RawData.ContainsKey("density")); + Assert.Null(model.Duration); + Assert.False(model.RawData.ContainsKey("duration")); + Assert.Null(model.Exif); + Assert.False(model.RawData.ContainsKey("exif")); + Assert.Null(model.Format); + Assert.False(model.RawData.ContainsKey("format")); + Assert.Null(model.HasColorProfile); + Assert.False(model.RawData.ContainsKey("hasColorProfile")); + Assert.Null(model.HasTransparency); + Assert.False(model.RawData.ContainsKey("hasTransparency")); + Assert.Null(model.Height); + Assert.False(model.RawData.ContainsKey("height")); + Assert.Null(model.PHash); + Assert.False(model.RawData.ContainsKey("pHash")); + Assert.Null(model.Quality); + Assert.False(model.RawData.ContainsKey("quality")); + Assert.Null(model.Size); + Assert.False(model.RawData.ContainsKey("size")); + Assert.Null(model.VideoCodec); + Assert.False(model.RawData.ContainsKey("videoCodec")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new FileMetadata { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new FileMetadata + { + // Null should be interpreted as omitted for these properties + AudioCodec = null, + BitRate = null, + Density = null, + Duration = null, + Exif = null, + Format = null, + HasColorProfile = null, + HasTransparency = null, + Height = null, + PHash = null, + Quality = null, + Size = null, + VideoCodec = null, + Width = null, + }; + + Assert.Null(model.AudioCodec); + Assert.False(model.RawData.ContainsKey("audioCodec")); + Assert.Null(model.BitRate); + Assert.False(model.RawData.ContainsKey("bitRate")); + Assert.Null(model.Density); + Assert.False(model.RawData.ContainsKey("density")); + Assert.Null(model.Duration); + Assert.False(model.RawData.ContainsKey("duration")); + Assert.Null(model.Exif); + Assert.False(model.RawData.ContainsKey("exif")); + Assert.Null(model.Format); + Assert.False(model.RawData.ContainsKey("format")); + Assert.Null(model.HasColorProfile); + Assert.False(model.RawData.ContainsKey("hasColorProfile")); + Assert.Null(model.HasTransparency); + Assert.False(model.RawData.ContainsKey("hasTransparency")); + Assert.Null(model.Height); + Assert.False(model.RawData.ContainsKey("height")); + Assert.Null(model.PHash); + Assert.False(model.RawData.ContainsKey("pHash")); + Assert.Null(model.Quality); + Assert.False(model.RawData.ContainsKey("quality")); + Assert.Null(model.Size); + Assert.False(model.RawData.ContainsKey("size")); + Assert.Null(model.VideoCodec); + Assert.False(model.RawData.ContainsKey("videoCodec")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new FileMetadata + { + // Null should be interpreted as omitted for these properties + AudioCodec = null, + BitRate = null, + Density = null, + Duration = null, + Exif = null, + Format = null, + HasColorProfile = null, + HasTransparency = null, + Height = null, + PHash = null, + Quality = null, + Size = null, + VideoCodec = null, + Width = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new FileMetadata + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }; + + FileMetadata copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExifTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Exif + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }; + + ExifExif expectedExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }; + Gps expectedGps = new() { GpsVersionID = [0] }; + Image expectedImage = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }; + Interoperability expectedInteroperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }; + Dictionary expectedMakernote = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + ExifThumbnail expectedThumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }; + + Assert.Equal(expectedExifValue, model.ExifValue); + Assert.Equal(expectedGps, model.Gps); + Assert.Equal(expectedImage, model.Image); + Assert.Equal(expectedInteroperability, model.Interoperability); + Assert.NotNull(model.Makernote); + Assert.Equal(expectedMakernote.Count, model.Makernote.Count); + foreach (var item in expectedMakernote) + { + Assert.True(model.Makernote.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, model.Makernote[item.Key])); + } + Assert.Equal(expectedThumbnail, model.Thumbnail); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Exif + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Exif + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + Assert.NotNull(deserialized); + + ExifExif expectedExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }; + Gps expectedGps = new() { GpsVersionID = [0] }; + Image expectedImage = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }; + Interoperability expectedInteroperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }; + Dictionary expectedMakernote = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + ExifThumbnail expectedThumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }; + + Assert.Equal(expectedExifValue, deserialized.ExifValue); + Assert.Equal(expectedGps, deserialized.Gps); + Assert.Equal(expectedImage, deserialized.Image); + Assert.Equal(expectedInteroperability, deserialized.Interoperability); + Assert.NotNull(deserialized.Makernote); + Assert.Equal(expectedMakernote.Count, deserialized.Makernote.Count); + foreach (var item in expectedMakernote) + { + Assert.True(deserialized.Makernote.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, deserialized.Makernote[item.Key])); + } + Assert.Equal(expectedThumbnail, deserialized.Thumbnail); + } + + [Fact] + public void Validation_Works() + { + var model = new Exif + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Exif { }; + + Assert.Null(model.ExifValue); + Assert.False(model.RawData.ContainsKey("exif")); + Assert.Null(model.Gps); + Assert.False(model.RawData.ContainsKey("gps")); + Assert.Null(model.Image); + Assert.False(model.RawData.ContainsKey("image")); + Assert.Null(model.Interoperability); + Assert.False(model.RawData.ContainsKey("interoperability")); + Assert.Null(model.Makernote); + Assert.False(model.RawData.ContainsKey("makernote")); + Assert.Null(model.Thumbnail); + Assert.False(model.RawData.ContainsKey("thumbnail")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Exif { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Exif + { + // Null should be interpreted as omitted for these properties + ExifValue = null, + Gps = null, + Image = null, + Interoperability = null, + Makernote = null, + Thumbnail = null, + }; + + Assert.Null(model.ExifValue); + Assert.False(model.RawData.ContainsKey("exif")); + Assert.Null(model.Gps); + Assert.False(model.RawData.ContainsKey("gps")); + Assert.Null(model.Image); + Assert.False(model.RawData.ContainsKey("image")); + Assert.Null(model.Interoperability); + Assert.False(model.RawData.ContainsKey("interoperability")); + Assert.Null(model.Makernote); + Assert.False(model.RawData.ContainsKey("makernote")); + Assert.Null(model.Thumbnail); + Assert.False(model.RawData.ContainsKey("thumbnail")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Exif + { + // Null should be interpreted as omitted for these properties + ExifValue = null, + Gps = null, + Image = null, + Interoperability = null, + Makernote = null, + Thumbnail = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Exif + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }; + + Exif copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExifExifTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExifExif + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }; + + double expectedApertureValue = 0; + long expectedColorSpace = 0; + string expectedCreateDate = "CreateDate"; + long expectedCustomRendered = 0; + string expectedDateTimeOriginal = "DateTimeOriginal"; + long expectedExifImageHeight = 0; + long expectedExifImageWidth = 0; + string expectedExifVersion = "ExifVersion"; + double expectedExposureCompensation = 0; + long expectedExposureMode = 0; + long expectedExposureProgram = 0; + double expectedExposureTime = 0; + long expectedFlash = 0; + string expectedFlashpixVersion = "FlashpixVersion"; + double expectedFNumber = 0; + long expectedFocalLength = 0; + long expectedFocalPlaneResolutionUnit = 0; + double expectedFocalPlaneXResolution = 0; + double expectedFocalPlaneYResolution = 0; + long expectedInteropOffset = 0; + long expectedIso = 0; + long expectedMeteringMode = 0; + long expectedSceneCaptureType = 0; + double expectedShutterSpeedValue = 0; + string expectedSubSecTime = "SubSecTime"; + long expectedWhiteBalance = 0; + + Assert.Equal(expectedApertureValue, model.ApertureValue); + Assert.Equal(expectedColorSpace, model.ColorSpace); + Assert.Equal(expectedCreateDate, model.CreateDate); + Assert.Equal(expectedCustomRendered, model.CustomRendered); + Assert.Equal(expectedDateTimeOriginal, model.DateTimeOriginal); + Assert.Equal(expectedExifImageHeight, model.ExifImageHeight); + Assert.Equal(expectedExifImageWidth, model.ExifImageWidth); + Assert.Equal(expectedExifVersion, model.ExifVersion); + Assert.Equal(expectedExposureCompensation, model.ExposureCompensation); + Assert.Equal(expectedExposureMode, model.ExposureMode); + Assert.Equal(expectedExposureProgram, model.ExposureProgram); + Assert.Equal(expectedExposureTime, model.ExposureTime); + Assert.Equal(expectedFlash, model.Flash); + Assert.Equal(expectedFlashpixVersion, model.FlashpixVersion); + Assert.Equal(expectedFNumber, model.FNumber); + Assert.Equal(expectedFocalLength, model.FocalLength); + Assert.Equal(expectedFocalPlaneResolutionUnit, model.FocalPlaneResolutionUnit); + Assert.Equal(expectedFocalPlaneXResolution, model.FocalPlaneXResolution); + Assert.Equal(expectedFocalPlaneYResolution, model.FocalPlaneYResolution); + Assert.Equal(expectedInteropOffset, model.InteropOffset); + Assert.Equal(expectedIso, model.Iso); + Assert.Equal(expectedMeteringMode, model.MeteringMode); + Assert.Equal(expectedSceneCaptureType, model.SceneCaptureType); + Assert.Equal(expectedShutterSpeedValue, model.ShutterSpeedValue); + Assert.Equal(expectedSubSecTime, model.SubSecTime); + Assert.Equal(expectedWhiteBalance, model.WhiteBalance); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExifExif + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExifExif + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + double expectedApertureValue = 0; + long expectedColorSpace = 0; + string expectedCreateDate = "CreateDate"; + long expectedCustomRendered = 0; + string expectedDateTimeOriginal = "DateTimeOriginal"; + long expectedExifImageHeight = 0; + long expectedExifImageWidth = 0; + string expectedExifVersion = "ExifVersion"; + double expectedExposureCompensation = 0; + long expectedExposureMode = 0; + long expectedExposureProgram = 0; + double expectedExposureTime = 0; + long expectedFlash = 0; + string expectedFlashpixVersion = "FlashpixVersion"; + double expectedFNumber = 0; + long expectedFocalLength = 0; + long expectedFocalPlaneResolutionUnit = 0; + double expectedFocalPlaneXResolution = 0; + double expectedFocalPlaneYResolution = 0; + long expectedInteropOffset = 0; + long expectedIso = 0; + long expectedMeteringMode = 0; + long expectedSceneCaptureType = 0; + double expectedShutterSpeedValue = 0; + string expectedSubSecTime = "SubSecTime"; + long expectedWhiteBalance = 0; + + Assert.Equal(expectedApertureValue, deserialized.ApertureValue); + Assert.Equal(expectedColorSpace, deserialized.ColorSpace); + Assert.Equal(expectedCreateDate, deserialized.CreateDate); + Assert.Equal(expectedCustomRendered, deserialized.CustomRendered); + Assert.Equal(expectedDateTimeOriginal, deserialized.DateTimeOriginal); + Assert.Equal(expectedExifImageHeight, deserialized.ExifImageHeight); + Assert.Equal(expectedExifImageWidth, deserialized.ExifImageWidth); + Assert.Equal(expectedExifVersion, deserialized.ExifVersion); + Assert.Equal(expectedExposureCompensation, deserialized.ExposureCompensation); + Assert.Equal(expectedExposureMode, deserialized.ExposureMode); + Assert.Equal(expectedExposureProgram, deserialized.ExposureProgram); + Assert.Equal(expectedExposureTime, deserialized.ExposureTime); + Assert.Equal(expectedFlash, deserialized.Flash); + Assert.Equal(expectedFlashpixVersion, deserialized.FlashpixVersion); + Assert.Equal(expectedFNumber, deserialized.FNumber); + Assert.Equal(expectedFocalLength, deserialized.FocalLength); + Assert.Equal(expectedFocalPlaneResolutionUnit, deserialized.FocalPlaneResolutionUnit); + Assert.Equal(expectedFocalPlaneXResolution, deserialized.FocalPlaneXResolution); + Assert.Equal(expectedFocalPlaneYResolution, deserialized.FocalPlaneYResolution); + Assert.Equal(expectedInteropOffset, deserialized.InteropOffset); + Assert.Equal(expectedIso, deserialized.Iso); + Assert.Equal(expectedMeteringMode, deserialized.MeteringMode); + Assert.Equal(expectedSceneCaptureType, deserialized.SceneCaptureType); + Assert.Equal(expectedShutterSpeedValue, deserialized.ShutterSpeedValue); + Assert.Equal(expectedSubSecTime, deserialized.SubSecTime); + Assert.Equal(expectedWhiteBalance, deserialized.WhiteBalance); + } + + [Fact] + public void Validation_Works() + { + var model = new ExifExif + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ExifExif { }; + + Assert.Null(model.ApertureValue); + Assert.False(model.RawData.ContainsKey("ApertureValue")); + Assert.Null(model.ColorSpace); + Assert.False(model.RawData.ContainsKey("ColorSpace")); + Assert.Null(model.CreateDate); + Assert.False(model.RawData.ContainsKey("CreateDate")); + Assert.Null(model.CustomRendered); + Assert.False(model.RawData.ContainsKey("CustomRendered")); + Assert.Null(model.DateTimeOriginal); + Assert.False(model.RawData.ContainsKey("DateTimeOriginal")); + Assert.Null(model.ExifImageHeight); + Assert.False(model.RawData.ContainsKey("ExifImageHeight")); + Assert.Null(model.ExifImageWidth); + Assert.False(model.RawData.ContainsKey("ExifImageWidth")); + Assert.Null(model.ExifVersion); + Assert.False(model.RawData.ContainsKey("ExifVersion")); + Assert.Null(model.ExposureCompensation); + Assert.False(model.RawData.ContainsKey("ExposureCompensation")); + Assert.Null(model.ExposureMode); + Assert.False(model.RawData.ContainsKey("ExposureMode")); + Assert.Null(model.ExposureProgram); + Assert.False(model.RawData.ContainsKey("ExposureProgram")); + Assert.Null(model.ExposureTime); + Assert.False(model.RawData.ContainsKey("ExposureTime")); + Assert.Null(model.Flash); + Assert.False(model.RawData.ContainsKey("Flash")); + Assert.Null(model.FlashpixVersion); + Assert.False(model.RawData.ContainsKey("FlashpixVersion")); + Assert.Null(model.FNumber); + Assert.False(model.RawData.ContainsKey("FNumber")); + Assert.Null(model.FocalLength); + Assert.False(model.RawData.ContainsKey("FocalLength")); + Assert.Null(model.FocalPlaneResolutionUnit); + Assert.False(model.RawData.ContainsKey("FocalPlaneResolutionUnit")); + Assert.Null(model.FocalPlaneXResolution); + Assert.False(model.RawData.ContainsKey("FocalPlaneXResolution")); + Assert.Null(model.FocalPlaneYResolution); + Assert.False(model.RawData.ContainsKey("FocalPlaneYResolution")); + Assert.Null(model.InteropOffset); + Assert.False(model.RawData.ContainsKey("InteropOffset")); + Assert.Null(model.Iso); + Assert.False(model.RawData.ContainsKey("ISO")); + Assert.Null(model.MeteringMode); + Assert.False(model.RawData.ContainsKey("MeteringMode")); + Assert.Null(model.SceneCaptureType); + Assert.False(model.RawData.ContainsKey("SceneCaptureType")); + Assert.Null(model.ShutterSpeedValue); + Assert.False(model.RawData.ContainsKey("ShutterSpeedValue")); + Assert.Null(model.SubSecTime); + Assert.False(model.RawData.ContainsKey("SubSecTime")); + Assert.Null(model.WhiteBalance); + Assert.False(model.RawData.ContainsKey("WhiteBalance")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new ExifExif { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ExifExif + { + // Null should be interpreted as omitted for these properties + ApertureValue = null, + ColorSpace = null, + CreateDate = null, + CustomRendered = null, + DateTimeOriginal = null, + ExifImageHeight = null, + ExifImageWidth = null, + ExifVersion = null, + ExposureCompensation = null, + ExposureMode = null, + ExposureProgram = null, + ExposureTime = null, + Flash = null, + FlashpixVersion = null, + FNumber = null, + FocalLength = null, + FocalPlaneResolutionUnit = null, + FocalPlaneXResolution = null, + FocalPlaneYResolution = null, + InteropOffset = null, + Iso = null, + MeteringMode = null, + SceneCaptureType = null, + ShutterSpeedValue = null, + SubSecTime = null, + WhiteBalance = null, + }; + + Assert.Null(model.ApertureValue); + Assert.False(model.RawData.ContainsKey("ApertureValue")); + Assert.Null(model.ColorSpace); + Assert.False(model.RawData.ContainsKey("ColorSpace")); + Assert.Null(model.CreateDate); + Assert.False(model.RawData.ContainsKey("CreateDate")); + Assert.Null(model.CustomRendered); + Assert.False(model.RawData.ContainsKey("CustomRendered")); + Assert.Null(model.DateTimeOriginal); + Assert.False(model.RawData.ContainsKey("DateTimeOriginal")); + Assert.Null(model.ExifImageHeight); + Assert.False(model.RawData.ContainsKey("ExifImageHeight")); + Assert.Null(model.ExifImageWidth); + Assert.False(model.RawData.ContainsKey("ExifImageWidth")); + Assert.Null(model.ExifVersion); + Assert.False(model.RawData.ContainsKey("ExifVersion")); + Assert.Null(model.ExposureCompensation); + Assert.False(model.RawData.ContainsKey("ExposureCompensation")); + Assert.Null(model.ExposureMode); + Assert.False(model.RawData.ContainsKey("ExposureMode")); + Assert.Null(model.ExposureProgram); + Assert.False(model.RawData.ContainsKey("ExposureProgram")); + Assert.Null(model.ExposureTime); + Assert.False(model.RawData.ContainsKey("ExposureTime")); + Assert.Null(model.Flash); + Assert.False(model.RawData.ContainsKey("Flash")); + Assert.Null(model.FlashpixVersion); + Assert.False(model.RawData.ContainsKey("FlashpixVersion")); + Assert.Null(model.FNumber); + Assert.False(model.RawData.ContainsKey("FNumber")); + Assert.Null(model.FocalLength); + Assert.False(model.RawData.ContainsKey("FocalLength")); + Assert.Null(model.FocalPlaneResolutionUnit); + Assert.False(model.RawData.ContainsKey("FocalPlaneResolutionUnit")); + Assert.Null(model.FocalPlaneXResolution); + Assert.False(model.RawData.ContainsKey("FocalPlaneXResolution")); + Assert.Null(model.FocalPlaneYResolution); + Assert.False(model.RawData.ContainsKey("FocalPlaneYResolution")); + Assert.Null(model.InteropOffset); + Assert.False(model.RawData.ContainsKey("InteropOffset")); + Assert.Null(model.Iso); + Assert.False(model.RawData.ContainsKey("ISO")); + Assert.Null(model.MeteringMode); + Assert.False(model.RawData.ContainsKey("MeteringMode")); + Assert.Null(model.SceneCaptureType); + Assert.False(model.RawData.ContainsKey("SceneCaptureType")); + Assert.Null(model.ShutterSpeedValue); + Assert.False(model.RawData.ContainsKey("ShutterSpeedValue")); + Assert.Null(model.SubSecTime); + Assert.False(model.RawData.ContainsKey("SubSecTime")); + Assert.Null(model.WhiteBalance); + Assert.False(model.RawData.ContainsKey("WhiteBalance")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new ExifExif + { + // Null should be interpreted as omitted for these properties + ApertureValue = null, + ColorSpace = null, + CreateDate = null, + CustomRendered = null, + DateTimeOriginal = null, + ExifImageHeight = null, + ExifImageWidth = null, + ExifVersion = null, + ExposureCompensation = null, + ExposureMode = null, + ExposureProgram = null, + ExposureTime = null, + Flash = null, + FlashpixVersion = null, + FNumber = null, + FocalLength = null, + FocalPlaneResolutionUnit = null, + FocalPlaneXResolution = null, + FocalPlaneYResolution = null, + InteropOffset = null, + Iso = null, + MeteringMode = null, + SceneCaptureType = null, + ShutterSpeedValue = null, + SubSecTime = null, + WhiteBalance = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExifExif + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }; + + ExifExif copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class GpsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Gps { GpsVersionID = [0] }; + + List expectedGpsVersionID = [0]; + + Assert.NotNull(model.GpsVersionID); + Assert.Equal(expectedGpsVersionID.Count, model.GpsVersionID.Count); + for (int i = 0; i < expectedGpsVersionID.Count; i++) + { + Assert.Equal(expectedGpsVersionID[i], model.GpsVersionID[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Gps { GpsVersionID = [0] }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Gps { GpsVersionID = [0] }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + Assert.NotNull(deserialized); + + List expectedGpsVersionID = [0]; + + Assert.NotNull(deserialized.GpsVersionID); + Assert.Equal(expectedGpsVersionID.Count, deserialized.GpsVersionID.Count); + for (int i = 0; i < expectedGpsVersionID.Count; i++) + { + Assert.Equal(expectedGpsVersionID[i], deserialized.GpsVersionID[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new Gps { GpsVersionID = [0] }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Gps { }; + + Assert.Null(model.GpsVersionID); + Assert.False(model.RawData.ContainsKey("GPSVersionID")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Gps { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Gps + { + // Null should be interpreted as omitted for these properties + GpsVersionID = null, + }; + + Assert.Null(model.GpsVersionID); + Assert.False(model.RawData.ContainsKey("GPSVersionID")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Gps + { + // Null should be interpreted as omitted for these properties + GpsVersionID = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Gps { GpsVersionID = [0] }; + + Gps copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ImageTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Image + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }; + + long expectedExifOffset = 0; + long expectedGpsInfo = 0; + string expectedMake = "Make"; + string expectedModel = "Model"; + string expectedModifyDate = "ModifyDate"; + long expectedOrientation = 0; + long expectedResolutionUnit = 0; + string expectedSoftware = "Software"; + long expectedXResolution = 0; + long expectedYCbCrPositioning = 0; + long expectedYResolution = 0; + + Assert.Equal(expectedExifOffset, model.ExifOffset); + Assert.Equal(expectedGpsInfo, model.GpsInfo); + Assert.Equal(expectedMake, model.Make); + Assert.Equal(expectedModel, model.Model); + Assert.Equal(expectedModifyDate, model.ModifyDate); + Assert.Equal(expectedOrientation, model.Orientation); + Assert.Equal(expectedResolutionUnit, model.ResolutionUnit); + Assert.Equal(expectedSoftware, model.Software); + Assert.Equal(expectedXResolution, model.XResolution); + Assert.Equal(expectedYCbCrPositioning, model.YCbCrPositioning); + Assert.Equal(expectedYResolution, model.YResolution); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Image + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Image + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + Assert.NotNull(deserialized); + + long expectedExifOffset = 0; + long expectedGpsInfo = 0; + string expectedMake = "Make"; + string expectedModel = "Model"; + string expectedModifyDate = "ModifyDate"; + long expectedOrientation = 0; + long expectedResolutionUnit = 0; + string expectedSoftware = "Software"; + long expectedXResolution = 0; + long expectedYCbCrPositioning = 0; + long expectedYResolution = 0; + + Assert.Equal(expectedExifOffset, deserialized.ExifOffset); + Assert.Equal(expectedGpsInfo, deserialized.GpsInfo); + Assert.Equal(expectedMake, deserialized.Make); + Assert.Equal(expectedModel, deserialized.Model); + Assert.Equal(expectedModifyDate, deserialized.ModifyDate); + Assert.Equal(expectedOrientation, deserialized.Orientation); + Assert.Equal(expectedResolutionUnit, deserialized.ResolutionUnit); + Assert.Equal(expectedSoftware, deserialized.Software); + Assert.Equal(expectedXResolution, deserialized.XResolution); + Assert.Equal(expectedYCbCrPositioning, deserialized.YCbCrPositioning); + Assert.Equal(expectedYResolution, deserialized.YResolution); + } + + [Fact] + public void Validation_Works() + { + var model = new Image + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Image { }; + + Assert.Null(model.ExifOffset); + Assert.False(model.RawData.ContainsKey("ExifOffset")); + Assert.Null(model.GpsInfo); + Assert.False(model.RawData.ContainsKey("GPSInfo")); + Assert.Null(model.Make); + Assert.False(model.RawData.ContainsKey("Make")); + Assert.Null(model.Model); + Assert.False(model.RawData.ContainsKey("Model")); + Assert.Null(model.ModifyDate); + Assert.False(model.RawData.ContainsKey("ModifyDate")); + Assert.Null(model.Orientation); + Assert.False(model.RawData.ContainsKey("Orientation")); + Assert.Null(model.ResolutionUnit); + Assert.False(model.RawData.ContainsKey("ResolutionUnit")); + Assert.Null(model.Software); + Assert.False(model.RawData.ContainsKey("Software")); + Assert.Null(model.XResolution); + Assert.False(model.RawData.ContainsKey("XResolution")); + Assert.Null(model.YCbCrPositioning); + Assert.False(model.RawData.ContainsKey("YCbCrPositioning")); + Assert.Null(model.YResolution); + Assert.False(model.RawData.ContainsKey("YResolution")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Image { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Image + { + // Null should be interpreted as omitted for these properties + ExifOffset = null, + GpsInfo = null, + Make = null, + Model = null, + ModifyDate = null, + Orientation = null, + ResolutionUnit = null, + Software = null, + XResolution = null, + YCbCrPositioning = null, + YResolution = null, + }; + + Assert.Null(model.ExifOffset); + Assert.False(model.RawData.ContainsKey("ExifOffset")); + Assert.Null(model.GpsInfo); + Assert.False(model.RawData.ContainsKey("GPSInfo")); + Assert.Null(model.Make); + Assert.False(model.RawData.ContainsKey("Make")); + Assert.Null(model.Model); + Assert.False(model.RawData.ContainsKey("Model")); + Assert.Null(model.ModifyDate); + Assert.False(model.RawData.ContainsKey("ModifyDate")); + Assert.Null(model.Orientation); + Assert.False(model.RawData.ContainsKey("Orientation")); + Assert.Null(model.ResolutionUnit); + Assert.False(model.RawData.ContainsKey("ResolutionUnit")); + Assert.Null(model.Software); + Assert.False(model.RawData.ContainsKey("Software")); + Assert.Null(model.XResolution); + Assert.False(model.RawData.ContainsKey("XResolution")); + Assert.Null(model.YCbCrPositioning); + Assert.False(model.RawData.ContainsKey("YCbCrPositioning")); + Assert.Null(model.YResolution); + Assert.False(model.RawData.ContainsKey("YResolution")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Image + { + // Null should be interpreted as omitted for these properties + ExifOffset = null, + GpsInfo = null, + Make = null, + Model = null, + ModifyDate = null, + Orientation = null, + ResolutionUnit = null, + Software = null, + XResolution = null, + YCbCrPositioning = null, + YResolution = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Image + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }; + + Image copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class InteroperabilityTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Interoperability + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }; + + string expectedInteropIndex = "InteropIndex"; + string expectedInteropVersion = "InteropVersion"; + + Assert.Equal(expectedInteropIndex, model.InteropIndex); + Assert.Equal(expectedInteropVersion, model.InteropVersion); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Interoperability + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Interoperability + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedInteropIndex = "InteropIndex"; + string expectedInteropVersion = "InteropVersion"; + + Assert.Equal(expectedInteropIndex, deserialized.InteropIndex); + Assert.Equal(expectedInteropVersion, deserialized.InteropVersion); + } + + [Fact] + public void Validation_Works() + { + var model = new Interoperability + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Interoperability { }; + + Assert.Null(model.InteropIndex); + Assert.False(model.RawData.ContainsKey("InteropIndex")); + Assert.Null(model.InteropVersion); + Assert.False(model.RawData.ContainsKey("InteropVersion")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Interoperability { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Interoperability + { + // Null should be interpreted as omitted for these properties + InteropIndex = null, + InteropVersion = null, + }; + + Assert.Null(model.InteropIndex); + Assert.False(model.RawData.ContainsKey("InteropIndex")); + Assert.Null(model.InteropVersion); + Assert.False(model.RawData.ContainsKey("InteropVersion")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Interoperability + { + // Null should be interpreted as omitted for these properties + InteropIndex = null, + InteropVersion = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Interoperability + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }; + + Interoperability copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExifThumbnailTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExifThumbnail + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }; + + long expectedCompression = 0; + long expectedResolutionUnit = 0; + long expectedThumbnailLength = 0; + long expectedThumbnailOffset = 0; + long expectedXResolution = 0; + long expectedYResolution = 0; + + Assert.Equal(expectedCompression, model.Compression); + Assert.Equal(expectedResolutionUnit, model.ResolutionUnit); + Assert.Equal(expectedThumbnailLength, model.ThumbnailLength); + Assert.Equal(expectedThumbnailOffset, model.ThumbnailOffset); + Assert.Equal(expectedXResolution, model.XResolution); + Assert.Equal(expectedYResolution, model.YResolution); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExifThumbnail + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExifThumbnail + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + long expectedCompression = 0; + long expectedResolutionUnit = 0; + long expectedThumbnailLength = 0; + long expectedThumbnailOffset = 0; + long expectedXResolution = 0; + long expectedYResolution = 0; + + Assert.Equal(expectedCompression, deserialized.Compression); + Assert.Equal(expectedResolutionUnit, deserialized.ResolutionUnit); + Assert.Equal(expectedThumbnailLength, deserialized.ThumbnailLength); + Assert.Equal(expectedThumbnailOffset, deserialized.ThumbnailOffset); + Assert.Equal(expectedXResolution, deserialized.XResolution); + Assert.Equal(expectedYResolution, deserialized.YResolution); + } + + [Fact] + public void Validation_Works() + { + var model = new ExifThumbnail + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ExifThumbnail { }; + + Assert.Null(model.Compression); + Assert.False(model.RawData.ContainsKey("Compression")); + Assert.Null(model.ResolutionUnit); + Assert.False(model.RawData.ContainsKey("ResolutionUnit")); + Assert.Null(model.ThumbnailLength); + Assert.False(model.RawData.ContainsKey("ThumbnailLength")); + Assert.Null(model.ThumbnailOffset); + Assert.False(model.RawData.ContainsKey("ThumbnailOffset")); + Assert.Null(model.XResolution); + Assert.False(model.RawData.ContainsKey("XResolution")); + Assert.Null(model.YResolution); + Assert.False(model.RawData.ContainsKey("YResolution")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new ExifThumbnail { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ExifThumbnail + { + // Null should be interpreted as omitted for these properties + Compression = null, + ResolutionUnit = null, + ThumbnailLength = null, + ThumbnailOffset = null, + XResolution = null, + YResolution = null, + }; + + Assert.Null(model.Compression); + Assert.False(model.RawData.ContainsKey("Compression")); + Assert.Null(model.ResolutionUnit); + Assert.False(model.RawData.ContainsKey("ResolutionUnit")); + Assert.Null(model.ThumbnailLength); + Assert.False(model.RawData.ContainsKey("ThumbnailLength")); + Assert.Null(model.ThumbnailOffset); + Assert.False(model.RawData.ContainsKey("ThumbnailOffset")); + Assert.Null(model.XResolution); + Assert.False(model.RawData.ContainsKey("XResolution")); + Assert.Null(model.YResolution); + Assert.False(model.RawData.ContainsKey("YResolution")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new ExifThumbnail + { + // Null should be interpreted as omitted for these properties + Compression = null, + ResolutionUnit = null, + ThumbnailLength = null, + ThumbnailOffset = null, + XResolution = null, + YResolution = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExifThumbnail + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }; + + ExifThumbnail copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/FileMoveParamsTest.cs b/src/Imagekit.Tests/Models/Files/FileMoveParamsTest.cs new file mode 100644 index 00000000..37fee500 --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/FileMoveParamsTest.cs @@ -0,0 +1,51 @@ +using System; +using Imagekit.Models.Files; + +namespace Imagekit.Tests.Models.Files; + +public class FileMoveParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new FileMoveParams + { + DestinationPath = "/folder/to/move/into/", + SourceFilePath = "/path/to/file.jpg", + }; + + string expectedDestinationPath = "/folder/to/move/into/"; + string expectedSourceFilePath = "/path/to/file.jpg"; + + Assert.Equal(expectedDestinationPath, parameters.DestinationPath); + Assert.Equal(expectedSourceFilePath, parameters.SourceFilePath); + } + + [Fact] + public void Url_Works() + { + FileMoveParams parameters = new() + { + DestinationPath = "/folder/to/move/into/", + SourceFilePath = "/path/to/file.jpg", + }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True(TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/files/move"), url)); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new FileMoveParams + { + DestinationPath = "/folder/to/move/into/", + SourceFilePath = "/path/to/file.jpg", + }; + + FileMoveParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/FileMoveResponseTest.cs b/src/Imagekit.Tests/Models/Files/FileMoveResponseTest.cs new file mode 100644 index 00000000..31f1e3da --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/FileMoveResponseTest.cs @@ -0,0 +1,59 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Files; + +namespace Imagekit.Tests.Models.Files; + +public class FileMoveResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new FileMoveResponse { }; + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new FileMoveResponse { }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new FileMoveResponse { }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + } + + [Fact] + public void Validation_Works() + { + var model = new FileMoveResponse { }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new FileMoveResponse { }; + + FileMoveResponse copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/FileRenameParamsTest.cs b/src/Imagekit.Tests/Models/Files/FileRenameParamsTest.cs new file mode 100644 index 00000000..a69220f8 --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/FileRenameParamsTest.cs @@ -0,0 +1,84 @@ +using System; +using Imagekit.Models.Files; + +namespace Imagekit.Tests.Models.Files; + +public class FileRenameParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new FileRenameParams + { + FilePath = "/path/to/file.jpg", + NewFileName = "newFileName.jpg", + PurgeCache = true, + }; + + string expectedFilePath = "/path/to/file.jpg"; + string expectedNewFileName = "newFileName.jpg"; + bool expectedPurgeCache = true; + + Assert.Equal(expectedFilePath, parameters.FilePath); + Assert.Equal(expectedNewFileName, parameters.NewFileName); + Assert.Equal(expectedPurgeCache, parameters.PurgeCache); + } + + [Fact] + public void OptionalNonNullableParamsUnsetAreNotSet_Works() + { + var parameters = new FileRenameParams + { + FilePath = "/path/to/file.jpg", + NewFileName = "newFileName.jpg", + }; + + Assert.Null(parameters.PurgeCache); + Assert.False(parameters.RawBodyData.ContainsKey("purgeCache")); + } + + [Fact] + public void OptionalNonNullableParamsSetToNullAreNotSet_Works() + { + var parameters = new FileRenameParams + { + FilePath = "/path/to/file.jpg", + NewFileName = "newFileName.jpg", + + // Null should be interpreted as omitted for these properties + PurgeCache = null, + }; + + Assert.Null(parameters.PurgeCache); + Assert.False(parameters.RawBodyData.ContainsKey("purgeCache")); + } + + [Fact] + public void Url_Works() + { + FileRenameParams parameters = new() + { + FilePath = "/path/to/file.jpg", + NewFileName = "newFileName.jpg", + }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True(TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/files/rename"), url)); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new FileRenameParams + { + FilePath = "/path/to/file.jpg", + NewFileName = "newFileName.jpg", + PurgeCache = true, + }; + + FileRenameParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/FileRenameResponseTest.cs b/src/Imagekit.Tests/Models/Files/FileRenameResponseTest.cs new file mode 100644 index 00000000..49ae2d01 --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/FileRenameResponseTest.cs @@ -0,0 +1,109 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Files; + +namespace Imagekit.Tests.Models.Files; + +public class FileRenameResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new FileRenameResponse { PurgeRequestID = "purgeRequestId" }; + + string expectedPurgeRequestID = "purgeRequestId"; + + Assert.Equal(expectedPurgeRequestID, model.PurgeRequestID); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new FileRenameResponse { PurgeRequestID = "purgeRequestId" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new FileRenameResponse { PurgeRequestID = "purgeRequestId" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedPurgeRequestID = "purgeRequestId"; + + Assert.Equal(expectedPurgeRequestID, deserialized.PurgeRequestID); + } + + [Fact] + public void Validation_Works() + { + var model = new FileRenameResponse { PurgeRequestID = "purgeRequestId" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new FileRenameResponse { }; + + Assert.Null(model.PurgeRequestID); + Assert.False(model.RawData.ContainsKey("purgeRequestId")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new FileRenameResponse { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new FileRenameResponse + { + // Null should be interpreted as omitted for these properties + PurgeRequestID = null, + }; + + Assert.Null(model.PurgeRequestID); + Assert.False(model.RawData.ContainsKey("purgeRequestId")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new FileRenameResponse + { + // Null should be interpreted as omitted for these properties + PurgeRequestID = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new FileRenameResponse { PurgeRequestID = "purgeRequestId" }; + + FileRenameResponse copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/FileTest.cs b/src/Imagekit.Tests/Models/Files/FileTest.cs new file mode 100644 index 00000000..f571d965 --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/FileTest.cs @@ -0,0 +1,1229 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Files = Imagekit.Models.Files; +using Models = Imagekit.Models; + +namespace Imagekit.Tests.Models.Files; + +public class FileTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Files::File + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + List expectedAITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ]; + string expectedAudioCodec = "audioCodec"; + long expectedBitRate = 0; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + string expectedCustomCoordinates = "customCoordinates"; + Dictionary expectedCustomMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + string expectedDescription = "description"; + long expectedDuration = 0; + Dictionary expectedEmbeddedMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + string expectedFileID = "fileId"; + string expectedFilePath = "filePath"; + string expectedFileType = "fileType"; + bool expectedHasAlpha = true; + double expectedHeight = 0; + bool expectedIsPrivateFile = true; + bool expectedIsPublished = true; + string expectedMime = "mime"; + string expectedName = "name"; + Dictionary expectedSelectedFieldsSchema = new() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }; + double expectedSize = 0; + List expectedTags = ["string"]; + string expectedThumbnail = "https://example.com"; + ApiEnum expectedType = Files::Type.File; + DateTimeOffset expectedUpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + string expectedUrl = "https://example.com"; + Models::VersionInfo expectedVersionInfo = new() { ID = "id", Name = "name" }; + string expectedVideoCodec = "videoCodec"; + double expectedWidth = 0; + + Assert.NotNull(model.AITags); + Assert.Equal(expectedAITags.Count, model.AITags.Count); + for (int i = 0; i < expectedAITags.Count; i++) + { + Assert.Equal(expectedAITags[i], model.AITags[i]); + } + Assert.Equal(expectedAudioCodec, model.AudioCodec); + Assert.Equal(expectedBitRate, model.BitRate); + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedCustomCoordinates, model.CustomCoordinates); + Assert.NotNull(model.CustomMetadata); + Assert.Equal(expectedCustomMetadata.Count, model.CustomMetadata.Count); + foreach (var item in expectedCustomMetadata) + { + Assert.True(model.CustomMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, model.CustomMetadata[item.Key])); + } + Assert.Equal(expectedDescription, model.Description); + Assert.Equal(expectedDuration, model.Duration); + Assert.NotNull(model.EmbeddedMetadata); + Assert.Equal(expectedEmbeddedMetadata.Count, model.EmbeddedMetadata.Count); + foreach (var item in expectedEmbeddedMetadata) + { + Assert.True(model.EmbeddedMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, model.EmbeddedMetadata[item.Key])); + } + Assert.Equal(expectedFileID, model.FileID); + Assert.Equal(expectedFilePath, model.FilePath); + Assert.Equal(expectedFileType, model.FileType); + Assert.Equal(expectedHasAlpha, model.HasAlpha); + Assert.Equal(expectedHeight, model.Height); + Assert.Equal(expectedIsPrivateFile, model.IsPrivateFile); + Assert.Equal(expectedIsPublished, model.IsPublished); + Assert.Equal(expectedMime, model.Mime); + Assert.Equal(expectedName, model.Name); + Assert.NotNull(model.SelectedFieldsSchema); + Assert.Equal(expectedSelectedFieldsSchema.Count, model.SelectedFieldsSchema.Count); + foreach (var item in expectedSelectedFieldsSchema) + { + Assert.True(model.SelectedFieldsSchema.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.SelectedFieldsSchema[item.Key]); + } + Assert.Equal(expectedSize, model.Size); + Assert.NotNull(model.Tags); + Assert.Equal(expectedTags.Count, model.Tags.Count); + for (int i = 0; i < expectedTags.Count; i++) + { + Assert.Equal(expectedTags[i], model.Tags[i]); + } + Assert.Equal(expectedThumbnail, model.Thumbnail); + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedUpdatedAt, model.UpdatedAt); + Assert.Equal(expectedUrl, model.Url); + Assert.Equal(expectedVersionInfo, model.VersionInfo); + Assert.Equal(expectedVideoCodec, model.VideoCodec); + Assert.Equal(expectedWidth, model.Width); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Files::File + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Files::File + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + List expectedAITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ]; + string expectedAudioCodec = "audioCodec"; + long expectedBitRate = 0; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + string expectedCustomCoordinates = "customCoordinates"; + Dictionary expectedCustomMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + string expectedDescription = "description"; + long expectedDuration = 0; + Dictionary expectedEmbeddedMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + string expectedFileID = "fileId"; + string expectedFilePath = "filePath"; + string expectedFileType = "fileType"; + bool expectedHasAlpha = true; + double expectedHeight = 0; + bool expectedIsPrivateFile = true; + bool expectedIsPublished = true; + string expectedMime = "mime"; + string expectedName = "name"; + Dictionary expectedSelectedFieldsSchema = new() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }; + double expectedSize = 0; + List expectedTags = ["string"]; + string expectedThumbnail = "https://example.com"; + ApiEnum expectedType = Files::Type.File; + DateTimeOffset expectedUpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + string expectedUrl = "https://example.com"; + Models::VersionInfo expectedVersionInfo = new() { ID = "id", Name = "name" }; + string expectedVideoCodec = "videoCodec"; + double expectedWidth = 0; + + Assert.NotNull(deserialized.AITags); + Assert.Equal(expectedAITags.Count, deserialized.AITags.Count); + for (int i = 0; i < expectedAITags.Count; i++) + { + Assert.Equal(expectedAITags[i], deserialized.AITags[i]); + } + Assert.Equal(expectedAudioCodec, deserialized.AudioCodec); + Assert.Equal(expectedBitRate, deserialized.BitRate); + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedCustomCoordinates, deserialized.CustomCoordinates); + Assert.NotNull(deserialized.CustomMetadata); + Assert.Equal(expectedCustomMetadata.Count, deserialized.CustomMetadata.Count); + foreach (var item in expectedCustomMetadata) + { + Assert.True(deserialized.CustomMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, deserialized.CustomMetadata[item.Key])); + } + Assert.Equal(expectedDescription, deserialized.Description); + Assert.Equal(expectedDuration, deserialized.Duration); + Assert.NotNull(deserialized.EmbeddedMetadata); + Assert.Equal(expectedEmbeddedMetadata.Count, deserialized.EmbeddedMetadata.Count); + foreach (var item in expectedEmbeddedMetadata) + { + Assert.True(deserialized.EmbeddedMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, deserialized.EmbeddedMetadata[item.Key])); + } + Assert.Equal(expectedFileID, deserialized.FileID); + Assert.Equal(expectedFilePath, deserialized.FilePath); + Assert.Equal(expectedFileType, deserialized.FileType); + Assert.Equal(expectedHasAlpha, deserialized.HasAlpha); + Assert.Equal(expectedHeight, deserialized.Height); + Assert.Equal(expectedIsPrivateFile, deserialized.IsPrivateFile); + Assert.Equal(expectedIsPublished, deserialized.IsPublished); + Assert.Equal(expectedMime, deserialized.Mime); + Assert.Equal(expectedName, deserialized.Name); + Assert.NotNull(deserialized.SelectedFieldsSchema); + Assert.Equal(expectedSelectedFieldsSchema.Count, deserialized.SelectedFieldsSchema.Count); + foreach (var item in expectedSelectedFieldsSchema) + { + Assert.True(deserialized.SelectedFieldsSchema.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.SelectedFieldsSchema[item.Key]); + } + Assert.Equal(expectedSize, deserialized.Size); + Assert.NotNull(deserialized.Tags); + Assert.Equal(expectedTags.Count, deserialized.Tags.Count); + for (int i = 0; i < expectedTags.Count; i++) + { + Assert.Equal(expectedTags[i], deserialized.Tags[i]); + } + Assert.Equal(expectedThumbnail, deserialized.Thumbnail); + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedUpdatedAt, deserialized.UpdatedAt); + Assert.Equal(expectedUrl, deserialized.Url); + Assert.Equal(expectedVersionInfo, deserialized.VersionInfo); + Assert.Equal(expectedVideoCodec, deserialized.VideoCodec); + Assert.Equal(expectedWidth, deserialized.Width); + } + + [Fact] + public void Validation_Works() + { + var model = new Files::File + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Files::File + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + CustomCoordinates = "customCoordinates", + Tags = ["string"], + }; + + Assert.Null(model.AudioCodec); + Assert.False(model.RawData.ContainsKey("audioCodec")); + Assert.Null(model.BitRate); + Assert.False(model.RawData.ContainsKey("bitRate")); + Assert.Null(model.CreatedAt); + Assert.False(model.RawData.ContainsKey("createdAt")); + Assert.Null(model.CustomMetadata); + Assert.False(model.RawData.ContainsKey("customMetadata")); + Assert.Null(model.Description); + Assert.False(model.RawData.ContainsKey("description")); + Assert.Null(model.Duration); + Assert.False(model.RawData.ContainsKey("duration")); + Assert.Null(model.EmbeddedMetadata); + Assert.False(model.RawData.ContainsKey("embeddedMetadata")); + Assert.Null(model.FileID); + Assert.False(model.RawData.ContainsKey("fileId")); + Assert.Null(model.FilePath); + Assert.False(model.RawData.ContainsKey("filePath")); + Assert.Null(model.FileType); + Assert.False(model.RawData.ContainsKey("fileType")); + Assert.Null(model.HasAlpha); + Assert.False(model.RawData.ContainsKey("hasAlpha")); + Assert.Null(model.Height); + Assert.False(model.RawData.ContainsKey("height")); + Assert.Null(model.IsPrivateFile); + Assert.False(model.RawData.ContainsKey("isPrivateFile")); + Assert.Null(model.IsPublished); + Assert.False(model.RawData.ContainsKey("isPublished")); + Assert.Null(model.Mime); + Assert.False(model.RawData.ContainsKey("mime")); + Assert.Null(model.Name); + Assert.False(model.RawData.ContainsKey("name")); + Assert.Null(model.SelectedFieldsSchema); + Assert.False(model.RawData.ContainsKey("selectedFieldsSchema")); + Assert.Null(model.Size); + Assert.False(model.RawData.ContainsKey("size")); + Assert.Null(model.Thumbnail); + Assert.False(model.RawData.ContainsKey("thumbnail")); + Assert.Null(model.Type); + Assert.False(model.RawData.ContainsKey("type")); + Assert.Null(model.UpdatedAt); + Assert.False(model.RawData.ContainsKey("updatedAt")); + Assert.Null(model.Url); + Assert.False(model.RawData.ContainsKey("url")); + Assert.Null(model.VersionInfo); + Assert.False(model.RawData.ContainsKey("versionInfo")); + Assert.Null(model.VideoCodec); + Assert.False(model.RawData.ContainsKey("videoCodec")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Files::File + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + CustomCoordinates = "customCoordinates", + Tags = ["string"], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Files::File + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + CustomCoordinates = "customCoordinates", + Tags = ["string"], + + // Null should be interpreted as omitted for these properties + AudioCodec = null, + BitRate = null, + CreatedAt = null, + CustomMetadata = null, + Description = null, + Duration = null, + EmbeddedMetadata = null, + FileID = null, + FilePath = null, + FileType = null, + HasAlpha = null, + Height = null, + IsPrivateFile = null, + IsPublished = null, + Mime = null, + Name = null, + SelectedFieldsSchema = null, + Size = null, + Thumbnail = null, + Type = null, + UpdatedAt = null, + Url = null, + VersionInfo = null, + VideoCodec = null, + Width = null, + }; + + Assert.Null(model.AudioCodec); + Assert.False(model.RawData.ContainsKey("audioCodec")); + Assert.Null(model.BitRate); + Assert.False(model.RawData.ContainsKey("bitRate")); + Assert.Null(model.CreatedAt); + Assert.False(model.RawData.ContainsKey("createdAt")); + Assert.Null(model.CustomMetadata); + Assert.False(model.RawData.ContainsKey("customMetadata")); + Assert.Null(model.Description); + Assert.False(model.RawData.ContainsKey("description")); + Assert.Null(model.Duration); + Assert.False(model.RawData.ContainsKey("duration")); + Assert.Null(model.EmbeddedMetadata); + Assert.False(model.RawData.ContainsKey("embeddedMetadata")); + Assert.Null(model.FileID); + Assert.False(model.RawData.ContainsKey("fileId")); + Assert.Null(model.FilePath); + Assert.False(model.RawData.ContainsKey("filePath")); + Assert.Null(model.FileType); + Assert.False(model.RawData.ContainsKey("fileType")); + Assert.Null(model.HasAlpha); + Assert.False(model.RawData.ContainsKey("hasAlpha")); + Assert.Null(model.Height); + Assert.False(model.RawData.ContainsKey("height")); + Assert.Null(model.IsPrivateFile); + Assert.False(model.RawData.ContainsKey("isPrivateFile")); + Assert.Null(model.IsPublished); + Assert.False(model.RawData.ContainsKey("isPublished")); + Assert.Null(model.Mime); + Assert.False(model.RawData.ContainsKey("mime")); + Assert.Null(model.Name); + Assert.False(model.RawData.ContainsKey("name")); + Assert.Null(model.SelectedFieldsSchema); + Assert.False(model.RawData.ContainsKey("selectedFieldsSchema")); + Assert.Null(model.Size); + Assert.False(model.RawData.ContainsKey("size")); + Assert.Null(model.Thumbnail); + Assert.False(model.RawData.ContainsKey("thumbnail")); + Assert.Null(model.Type); + Assert.False(model.RawData.ContainsKey("type")); + Assert.Null(model.UpdatedAt); + Assert.False(model.RawData.ContainsKey("updatedAt")); + Assert.Null(model.Url); + Assert.False(model.RawData.ContainsKey("url")); + Assert.Null(model.VersionInfo); + Assert.False(model.RawData.ContainsKey("versionInfo")); + Assert.Null(model.VideoCodec); + Assert.False(model.RawData.ContainsKey("videoCodec")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Files::File + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + CustomCoordinates = "customCoordinates", + Tags = ["string"], + + // Null should be interpreted as omitted for these properties + AudioCodec = null, + BitRate = null, + CreatedAt = null, + CustomMetadata = null, + Description = null, + Duration = null, + EmbeddedMetadata = null, + FileID = null, + FilePath = null, + FileType = null, + HasAlpha = null, + Height = null, + IsPrivateFile = null, + IsPublished = null, + Mime = null, + Name = null, + SelectedFieldsSchema = null, + Size = null, + Thumbnail = null, + Type = null, + UpdatedAt = null, + Url = null, + VersionInfo = null, + VideoCodec = null, + Width = null, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Files::File + { + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + Assert.Null(model.AITags); + Assert.False(model.RawData.ContainsKey("AITags")); + Assert.Null(model.CustomCoordinates); + Assert.False(model.RawData.ContainsKey("customCoordinates")); + Assert.Null(model.Tags); + Assert.False(model.RawData.ContainsKey("tags")); + } + + [Fact] + public void OptionalNullablePropertiesUnsetValidation_Works() + { + var model = new Files::File + { + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNullablePropertiesSetToNullAreSetToNull_Works() + { + var model = new Files::File + { + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + + AITags = null, + CustomCoordinates = null, + Tags = null, + }; + + Assert.Null(model.AITags); + Assert.True(model.RawData.ContainsKey("AITags")); + Assert.Null(model.CustomCoordinates); + Assert.True(model.RawData.ContainsKey("customCoordinates")); + Assert.Null(model.Tags); + Assert.True(model.RawData.ContainsKey("tags")); + } + + [Fact] + public void OptionalNullablePropertiesSetToNullValidation_Works() + { + var model = new Files::File + { + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + + AITags = null, + CustomCoordinates = null, + Tags = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Files::File + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + Files::File copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class TypeTest : TestBase +{ + [Theory] + [InlineData(Files::Type.File)] + [InlineData(Files::Type.FileVersion)] + public void Validation_Works(Files::Type rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Files::Type.File)] + [InlineData(Files::Type.FileVersion)] + public void SerializationRoundtrip_Works(Files::Type rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/Files/FileUpdateParamsTest.cs b/src/Imagekit.Tests/Models/Files/FileUpdateParamsTest.cs new file mode 100644 index 00000000..c78b95f8 --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/FileUpdateParamsTest.cs @@ -0,0 +1,256 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Models; +using Imagekit.Models.Files; + +namespace Imagekit.Tests.Models.Files; + +public class FileUpdateParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new FileUpdateParams + { + FileID = "fileId", + UpdateFileRequest = new UpdateFileDetails() + { + CustomCoordinates = "10,10,100,100", + CustomMetadata = new Dictionary() + { + { "brand", JsonSerializer.SerializeToElement("bar") }, + { "color", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Extensions = + [ + new ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 10, + MinConfidence = 80, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 10, + MinConfidence = 80, + Name = ExtensionItemAutoTaggingExtensionName.AwsAutoTagging, + }, + new ExtensionItemAIAutoDescription(), + new ExtensionItemAITasks( + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "dress", "jacket"], + }, + ] + ), + new SavedExtension("ext_abc123"), + ], + RemoveAITags = new(["car", "vehicle", "motorsports"]), + Tags = ["tag1", "tag2"], + WebhookUrl = "https://webhook.site/0d6b6c7a-8e5a-4b3a-8b7c-0d6b6c7a8e5a", + }, + }; + + string expectedFileID = "fileId"; + UpdateFileRequest expectedUpdateFileRequest = new UpdateFileDetails() + { + CustomCoordinates = "10,10,100,100", + CustomMetadata = new Dictionary() + { + { "brand", JsonSerializer.SerializeToElement("bar") }, + { "color", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Extensions = + [ + new ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 10, + MinConfidence = 80, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 10, + MinConfidence = 80, + Name = ExtensionItemAutoTaggingExtensionName.AwsAutoTagging, + }, + new ExtensionItemAIAutoDescription(), + new ExtensionItemAITasks( + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "dress", "jacket"], + }, + ] + ), + new SavedExtension("ext_abc123"), + ], + RemoveAITags = new(["car", "vehicle", "motorsports"]), + Tags = ["tag1", "tag2"], + WebhookUrl = "https://webhook.site/0d6b6c7a-8e5a-4b3a-8b7c-0d6b6c7a8e5a", + }; + + Assert.Equal(expectedFileID, parameters.FileID); + Assert.Equal(expectedUpdateFileRequest, parameters.UpdateFileRequest); + } + + [Fact] + public void Url_Works() + { + FileUpdateParams parameters = new() + { + FileID = "fileId", + UpdateFileRequest = new UpdateFileDetails() + { + CustomCoordinates = "10,10,100,100", + CustomMetadata = new Dictionary() + { + { "brand", JsonSerializer.SerializeToElement("bar") }, + { "color", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Extensions = + [ + new ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 10, + MinConfidence = 80, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 10, + MinConfidence = 80, + Name = ExtensionItemAutoTaggingExtensionName.AwsAutoTagging, + }, + new ExtensionItemAIAutoDescription(), + new ExtensionItemAITasks( + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "dress", "jacket"], + }, + ] + ), + new SavedExtension("ext_abc123"), + ], + RemoveAITags = new(["car", "vehicle", "motorsports"]), + Tags = ["tag1", "tag2"], + WebhookUrl = "https://webhook.site/0d6b6c7a-8e5a-4b3a-8b7c-0d6b6c7a8e5a", + }, + }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/files/fileId/details"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new FileUpdateParams + { + FileID = "fileId", + UpdateFileRequest = new UpdateFileDetails() + { + CustomCoordinates = "10,10,100,100", + CustomMetadata = new Dictionary() + { + { "brand", JsonSerializer.SerializeToElement("bar") }, + { "color", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Extensions = + [ + new ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 10, + MinConfidence = 80, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 10, + MinConfidence = 80, + Name = ExtensionItemAutoTaggingExtensionName.AwsAutoTagging, + }, + new ExtensionItemAIAutoDescription(), + new ExtensionItemAITasks( + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "dress", "jacket"], + }, + ] + ), + new SavedExtension("ext_abc123"), + ], + RemoveAITags = new(["car", "vehicle", "motorsports"]), + Tags = ["tag1", "tag2"], + WebhookUrl = "https://webhook.site/0d6b6c7a-8e5a-4b3a-8b7c-0d6b6c7a8e5a", + }, + }; + + FileUpdateParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/FileUpdateResponseTest.cs b/src/Imagekit.Tests/Models/Files/FileUpdateResponseTest.cs new file mode 100644 index 00000000..017e2cda --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/FileUpdateResponseTest.cs @@ -0,0 +1,1920 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Files = Imagekit.Models.Files; +using Models = Imagekit.Models; + +namespace Imagekit.Tests.Models.Files; + +public class FileUpdateResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Files::FileUpdateResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + }; + + List expectedAITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ]; + string expectedAudioCodec = "audioCodec"; + long expectedBitRate = 0; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + string expectedCustomCoordinates = "customCoordinates"; + Dictionary expectedCustomMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + string expectedDescription = "description"; + long expectedDuration = 0; + Dictionary expectedEmbeddedMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + string expectedFileID = "fileId"; + string expectedFilePath = "filePath"; + string expectedFileType = "fileType"; + bool expectedHasAlpha = true; + double expectedHeight = 0; + bool expectedIsPrivateFile = true; + bool expectedIsPublished = true; + string expectedMime = "mime"; + string expectedName = "name"; + Dictionary expectedSelectedFieldsSchema = new() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }; + double expectedSize = 0; + List expectedTags = ["string"]; + string expectedThumbnail = "https://example.com"; + ApiEnum expectedType = Files::Type.File; + DateTimeOffset expectedUpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + string expectedUrl = "https://example.com"; + Models::VersionInfo expectedVersionInfo = new() { ID = "id", Name = "name" }; + string expectedVideoCodec = "videoCodec"; + double expectedWidth = 0; + Files::ExtensionStatus expectedExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }; + + Assert.NotNull(model.AITags); + Assert.Equal(expectedAITags.Count, model.AITags.Count); + for (int i = 0; i < expectedAITags.Count; i++) + { + Assert.Equal(expectedAITags[i], model.AITags[i]); + } + Assert.Equal(expectedAudioCodec, model.AudioCodec); + Assert.Equal(expectedBitRate, model.BitRate); + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedCustomCoordinates, model.CustomCoordinates); + Assert.NotNull(model.CustomMetadata); + Assert.Equal(expectedCustomMetadata.Count, model.CustomMetadata.Count); + foreach (var item in expectedCustomMetadata) + { + Assert.True(model.CustomMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, model.CustomMetadata[item.Key])); + } + Assert.Equal(expectedDescription, model.Description); + Assert.Equal(expectedDuration, model.Duration); + Assert.NotNull(model.EmbeddedMetadata); + Assert.Equal(expectedEmbeddedMetadata.Count, model.EmbeddedMetadata.Count); + foreach (var item in expectedEmbeddedMetadata) + { + Assert.True(model.EmbeddedMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, model.EmbeddedMetadata[item.Key])); + } + Assert.Equal(expectedFileID, model.FileID); + Assert.Equal(expectedFilePath, model.FilePath); + Assert.Equal(expectedFileType, model.FileType); + Assert.Equal(expectedHasAlpha, model.HasAlpha); + Assert.Equal(expectedHeight, model.Height); + Assert.Equal(expectedIsPrivateFile, model.IsPrivateFile); + Assert.Equal(expectedIsPublished, model.IsPublished); + Assert.Equal(expectedMime, model.Mime); + Assert.Equal(expectedName, model.Name); + Assert.NotNull(model.SelectedFieldsSchema); + Assert.Equal(expectedSelectedFieldsSchema.Count, model.SelectedFieldsSchema.Count); + foreach (var item in expectedSelectedFieldsSchema) + { + Assert.True(model.SelectedFieldsSchema.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.SelectedFieldsSchema[item.Key]); + } + Assert.Equal(expectedSize, model.Size); + Assert.NotNull(model.Tags); + Assert.Equal(expectedTags.Count, model.Tags.Count); + for (int i = 0; i < expectedTags.Count; i++) + { + Assert.Equal(expectedTags[i], model.Tags[i]); + } + Assert.Equal(expectedThumbnail, model.Thumbnail); + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedUpdatedAt, model.UpdatedAt); + Assert.Equal(expectedUrl, model.Url); + Assert.Equal(expectedVersionInfo, model.VersionInfo); + Assert.Equal(expectedVideoCodec, model.VideoCodec); + Assert.Equal(expectedWidth, model.Width); + Assert.Equal(expectedExtensionStatus, model.ExtensionStatus); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Files::FileUpdateResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Files::FileUpdateResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + List expectedAITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ]; + string expectedAudioCodec = "audioCodec"; + long expectedBitRate = 0; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + string expectedCustomCoordinates = "customCoordinates"; + Dictionary expectedCustomMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + string expectedDescription = "description"; + long expectedDuration = 0; + Dictionary expectedEmbeddedMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + string expectedFileID = "fileId"; + string expectedFilePath = "filePath"; + string expectedFileType = "fileType"; + bool expectedHasAlpha = true; + double expectedHeight = 0; + bool expectedIsPrivateFile = true; + bool expectedIsPublished = true; + string expectedMime = "mime"; + string expectedName = "name"; + Dictionary expectedSelectedFieldsSchema = new() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }; + double expectedSize = 0; + List expectedTags = ["string"]; + string expectedThumbnail = "https://example.com"; + ApiEnum expectedType = Files::Type.File; + DateTimeOffset expectedUpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + string expectedUrl = "https://example.com"; + Models::VersionInfo expectedVersionInfo = new() { ID = "id", Name = "name" }; + string expectedVideoCodec = "videoCodec"; + double expectedWidth = 0; + Files::ExtensionStatus expectedExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }; + + Assert.NotNull(deserialized.AITags); + Assert.Equal(expectedAITags.Count, deserialized.AITags.Count); + for (int i = 0; i < expectedAITags.Count; i++) + { + Assert.Equal(expectedAITags[i], deserialized.AITags[i]); + } + Assert.Equal(expectedAudioCodec, deserialized.AudioCodec); + Assert.Equal(expectedBitRate, deserialized.BitRate); + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedCustomCoordinates, deserialized.CustomCoordinates); + Assert.NotNull(deserialized.CustomMetadata); + Assert.Equal(expectedCustomMetadata.Count, deserialized.CustomMetadata.Count); + foreach (var item in expectedCustomMetadata) + { + Assert.True(deserialized.CustomMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, deserialized.CustomMetadata[item.Key])); + } + Assert.Equal(expectedDescription, deserialized.Description); + Assert.Equal(expectedDuration, deserialized.Duration); + Assert.NotNull(deserialized.EmbeddedMetadata); + Assert.Equal(expectedEmbeddedMetadata.Count, deserialized.EmbeddedMetadata.Count); + foreach (var item in expectedEmbeddedMetadata) + { + Assert.True(deserialized.EmbeddedMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, deserialized.EmbeddedMetadata[item.Key])); + } + Assert.Equal(expectedFileID, deserialized.FileID); + Assert.Equal(expectedFilePath, deserialized.FilePath); + Assert.Equal(expectedFileType, deserialized.FileType); + Assert.Equal(expectedHasAlpha, deserialized.HasAlpha); + Assert.Equal(expectedHeight, deserialized.Height); + Assert.Equal(expectedIsPrivateFile, deserialized.IsPrivateFile); + Assert.Equal(expectedIsPublished, deserialized.IsPublished); + Assert.Equal(expectedMime, deserialized.Mime); + Assert.Equal(expectedName, deserialized.Name); + Assert.NotNull(deserialized.SelectedFieldsSchema); + Assert.Equal(expectedSelectedFieldsSchema.Count, deserialized.SelectedFieldsSchema.Count); + foreach (var item in expectedSelectedFieldsSchema) + { + Assert.True(deserialized.SelectedFieldsSchema.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.SelectedFieldsSchema[item.Key]); + } + Assert.Equal(expectedSize, deserialized.Size); + Assert.NotNull(deserialized.Tags); + Assert.Equal(expectedTags.Count, deserialized.Tags.Count); + for (int i = 0; i < expectedTags.Count; i++) + { + Assert.Equal(expectedTags[i], deserialized.Tags[i]); + } + Assert.Equal(expectedThumbnail, deserialized.Thumbnail); + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedUpdatedAt, deserialized.UpdatedAt); + Assert.Equal(expectedUrl, deserialized.Url); + Assert.Equal(expectedVersionInfo, deserialized.VersionInfo); + Assert.Equal(expectedVideoCodec, deserialized.VideoCodec); + Assert.Equal(expectedWidth, deserialized.Width); + Assert.Equal(expectedExtensionStatus, deserialized.ExtensionStatus); + } + + [Fact] + public void Validation_Works() + { + var model = new Files::FileUpdateResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Files::FileUpdateResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + CustomCoordinates = "customCoordinates", + Tags = ["string"], + }; + + Assert.Null(model.AudioCodec); + Assert.False(model.RawData.ContainsKey("audioCodec")); + Assert.Null(model.BitRate); + Assert.False(model.RawData.ContainsKey("bitRate")); + Assert.Null(model.CreatedAt); + Assert.False(model.RawData.ContainsKey("createdAt")); + Assert.Null(model.CustomMetadata); + Assert.False(model.RawData.ContainsKey("customMetadata")); + Assert.Null(model.Description); + Assert.False(model.RawData.ContainsKey("description")); + Assert.Null(model.Duration); + Assert.False(model.RawData.ContainsKey("duration")); + Assert.Null(model.EmbeddedMetadata); + Assert.False(model.RawData.ContainsKey("embeddedMetadata")); + Assert.Null(model.FileID); + Assert.False(model.RawData.ContainsKey("fileId")); + Assert.Null(model.FilePath); + Assert.False(model.RawData.ContainsKey("filePath")); + Assert.Null(model.FileType); + Assert.False(model.RawData.ContainsKey("fileType")); + Assert.Null(model.HasAlpha); + Assert.False(model.RawData.ContainsKey("hasAlpha")); + Assert.Null(model.Height); + Assert.False(model.RawData.ContainsKey("height")); + Assert.Null(model.IsPrivateFile); + Assert.False(model.RawData.ContainsKey("isPrivateFile")); + Assert.Null(model.IsPublished); + Assert.False(model.RawData.ContainsKey("isPublished")); + Assert.Null(model.Mime); + Assert.False(model.RawData.ContainsKey("mime")); + Assert.Null(model.Name); + Assert.False(model.RawData.ContainsKey("name")); + Assert.Null(model.SelectedFieldsSchema); + Assert.False(model.RawData.ContainsKey("selectedFieldsSchema")); + Assert.Null(model.Size); + Assert.False(model.RawData.ContainsKey("size")); + Assert.Null(model.Thumbnail); + Assert.False(model.RawData.ContainsKey("thumbnail")); + Assert.Null(model.Type); + Assert.False(model.RawData.ContainsKey("type")); + Assert.Null(model.UpdatedAt); + Assert.False(model.RawData.ContainsKey("updatedAt")); + Assert.Null(model.Url); + Assert.False(model.RawData.ContainsKey("url")); + Assert.Null(model.VersionInfo); + Assert.False(model.RawData.ContainsKey("versionInfo")); + Assert.Null(model.VideoCodec); + Assert.False(model.RawData.ContainsKey("videoCodec")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + Assert.Null(model.ExtensionStatus); + Assert.False(model.RawData.ContainsKey("extensionStatus")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Files::FileUpdateResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + CustomCoordinates = "customCoordinates", + Tags = ["string"], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Files::FileUpdateResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + CustomCoordinates = "customCoordinates", + Tags = ["string"], + + // Null should be interpreted as omitted for these properties + AudioCodec = null, + BitRate = null, + CreatedAt = null, + CustomMetadata = null, + Description = null, + Duration = null, + EmbeddedMetadata = null, + FileID = null, + FilePath = null, + FileType = null, + HasAlpha = null, + Height = null, + IsPrivateFile = null, + IsPublished = null, + Mime = null, + Name = null, + SelectedFieldsSchema = null, + Size = null, + Thumbnail = null, + Type = null, + UpdatedAt = null, + Url = null, + VersionInfo = null, + VideoCodec = null, + Width = null, + ExtensionStatus = null, + }; + + Assert.Null(model.AudioCodec); + Assert.False(model.RawData.ContainsKey("audioCodec")); + Assert.Null(model.BitRate); + Assert.False(model.RawData.ContainsKey("bitRate")); + Assert.Null(model.CreatedAt); + Assert.False(model.RawData.ContainsKey("createdAt")); + Assert.Null(model.CustomMetadata); + Assert.False(model.RawData.ContainsKey("customMetadata")); + Assert.Null(model.Description); + Assert.False(model.RawData.ContainsKey("description")); + Assert.Null(model.Duration); + Assert.False(model.RawData.ContainsKey("duration")); + Assert.Null(model.EmbeddedMetadata); + Assert.False(model.RawData.ContainsKey("embeddedMetadata")); + Assert.Null(model.FileID); + Assert.False(model.RawData.ContainsKey("fileId")); + Assert.Null(model.FilePath); + Assert.False(model.RawData.ContainsKey("filePath")); + Assert.Null(model.FileType); + Assert.False(model.RawData.ContainsKey("fileType")); + Assert.Null(model.HasAlpha); + Assert.False(model.RawData.ContainsKey("hasAlpha")); + Assert.Null(model.Height); + Assert.False(model.RawData.ContainsKey("height")); + Assert.Null(model.IsPrivateFile); + Assert.False(model.RawData.ContainsKey("isPrivateFile")); + Assert.Null(model.IsPublished); + Assert.False(model.RawData.ContainsKey("isPublished")); + Assert.Null(model.Mime); + Assert.False(model.RawData.ContainsKey("mime")); + Assert.Null(model.Name); + Assert.False(model.RawData.ContainsKey("name")); + Assert.Null(model.SelectedFieldsSchema); + Assert.False(model.RawData.ContainsKey("selectedFieldsSchema")); + Assert.Null(model.Size); + Assert.False(model.RawData.ContainsKey("size")); + Assert.Null(model.Thumbnail); + Assert.False(model.RawData.ContainsKey("thumbnail")); + Assert.Null(model.Type); + Assert.False(model.RawData.ContainsKey("type")); + Assert.Null(model.UpdatedAt); + Assert.False(model.RawData.ContainsKey("updatedAt")); + Assert.Null(model.Url); + Assert.False(model.RawData.ContainsKey("url")); + Assert.Null(model.VersionInfo); + Assert.False(model.RawData.ContainsKey("versionInfo")); + Assert.Null(model.VideoCodec); + Assert.False(model.RawData.ContainsKey("videoCodec")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + Assert.Null(model.ExtensionStatus); + Assert.False(model.RawData.ContainsKey("extensionStatus")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Files::FileUpdateResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + CustomCoordinates = "customCoordinates", + Tags = ["string"], + + // Null should be interpreted as omitted for these properties + AudioCodec = null, + BitRate = null, + CreatedAt = null, + CustomMetadata = null, + Description = null, + Duration = null, + EmbeddedMetadata = null, + FileID = null, + FilePath = null, + FileType = null, + HasAlpha = null, + Height = null, + IsPrivateFile = null, + IsPublished = null, + Mime = null, + Name = null, + SelectedFieldsSchema = null, + Size = null, + Thumbnail = null, + Type = null, + UpdatedAt = null, + Url = null, + VersionInfo = null, + VideoCodec = null, + Width = null, + ExtensionStatus = null, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Files::FileUpdateResponse + { + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + }; + + Assert.Null(model.AITags); + Assert.False(model.RawData.ContainsKey("AITags")); + Assert.Null(model.CustomCoordinates); + Assert.False(model.RawData.ContainsKey("customCoordinates")); + Assert.Null(model.Tags); + Assert.False(model.RawData.ContainsKey("tags")); + } + + [Fact] + public void OptionalNullablePropertiesUnsetValidation_Works() + { + var model = new Files::FileUpdateResponse + { + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNullablePropertiesSetToNullAreSetToNull_Works() + { + var model = new Files::FileUpdateResponse + { + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + + AITags = null, + CustomCoordinates = null, + Tags = null, + }; + + Assert.Null(model.AITags); + Assert.True(model.RawData.ContainsKey("AITags")); + Assert.Null(model.CustomCoordinates); + Assert.True(model.RawData.ContainsKey("customCoordinates")); + Assert.Null(model.Tags); + Assert.True(model.RawData.ContainsKey("tags")); + } + + [Fact] + public void OptionalNullablePropertiesSetToNullValidation_Works() + { + var model = new Files::FileUpdateResponse + { + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + + AITags = null, + CustomCoordinates = null, + Tags = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Files::FileUpdateResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + }; + + Files::FileUpdateResponse copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class FileUpdateResponseFileUpdateResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Files::FileUpdateResponseFileUpdateResponse + { + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + }; + + Files::ExtensionStatus expectedExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }; + + Assert.Equal(expectedExtensionStatus, model.ExtensionStatus); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Files::FileUpdateResponseFileUpdateResponse + { + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Files::FileUpdateResponseFileUpdateResponse + { + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + Files::ExtensionStatus expectedExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }; + + Assert.Equal(expectedExtensionStatus, deserialized.ExtensionStatus); + } + + [Fact] + public void Validation_Works() + { + var model = new Files::FileUpdateResponseFileUpdateResponse + { + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Files::FileUpdateResponseFileUpdateResponse { }; + + Assert.Null(model.ExtensionStatus); + Assert.False(model.RawData.ContainsKey("extensionStatus")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Files::FileUpdateResponseFileUpdateResponse { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Files::FileUpdateResponseFileUpdateResponse + { + // Null should be interpreted as omitted for these properties + ExtensionStatus = null, + }; + + Assert.Null(model.ExtensionStatus); + Assert.False(model.RawData.ContainsKey("extensionStatus")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Files::FileUpdateResponseFileUpdateResponse + { + // Null should be interpreted as omitted for these properties + ExtensionStatus = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Files::FileUpdateResponseFileUpdateResponse + { + ExtensionStatus = new() + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }, + }; + + Files::FileUpdateResponseFileUpdateResponse copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExtensionStatusTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Files::ExtensionStatus + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }; + + ApiEnum expectedAIAutoDescription = + Files::AIAutoDescription.Success; + ApiEnum expectedAITasks = Files::AITasks.Success; + ApiEnum expectedAwsAutoTagging = + Files::AwsAutoTagging.Success; + ApiEnum expectedGoogleAutoTagging = + Files::GoogleAutoTagging.Success; + ApiEnum expectedRemoveBg = Files::RemoveBg.Success; + + Assert.Equal(expectedAIAutoDescription, model.AIAutoDescription); + Assert.Equal(expectedAITasks, model.AITasks); + Assert.Equal(expectedAwsAutoTagging, model.AwsAutoTagging); + Assert.Equal(expectedGoogleAutoTagging, model.GoogleAutoTagging); + Assert.Equal(expectedRemoveBg, model.RemoveBg); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Files::ExtensionStatus + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Files::ExtensionStatus + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum expectedAIAutoDescription = + Files::AIAutoDescription.Success; + ApiEnum expectedAITasks = Files::AITasks.Success; + ApiEnum expectedAwsAutoTagging = + Files::AwsAutoTagging.Success; + ApiEnum expectedGoogleAutoTagging = + Files::GoogleAutoTagging.Success; + ApiEnum expectedRemoveBg = Files::RemoveBg.Success; + + Assert.Equal(expectedAIAutoDescription, deserialized.AIAutoDescription); + Assert.Equal(expectedAITasks, deserialized.AITasks); + Assert.Equal(expectedAwsAutoTagging, deserialized.AwsAutoTagging); + Assert.Equal(expectedGoogleAutoTagging, deserialized.GoogleAutoTagging); + Assert.Equal(expectedRemoveBg, deserialized.RemoveBg); + } + + [Fact] + public void Validation_Works() + { + var model = new Files::ExtensionStatus + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Files::ExtensionStatus { }; + + Assert.Null(model.AIAutoDescription); + Assert.False(model.RawData.ContainsKey("ai-auto-description")); + Assert.Null(model.AITasks); + Assert.False(model.RawData.ContainsKey("ai-tasks")); + Assert.Null(model.AwsAutoTagging); + Assert.False(model.RawData.ContainsKey("aws-auto-tagging")); + Assert.Null(model.GoogleAutoTagging); + Assert.False(model.RawData.ContainsKey("google-auto-tagging")); + Assert.Null(model.RemoveBg); + Assert.False(model.RawData.ContainsKey("remove-bg")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Files::ExtensionStatus { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Files::ExtensionStatus + { + // Null should be interpreted as omitted for these properties + AIAutoDescription = null, + AITasks = null, + AwsAutoTagging = null, + GoogleAutoTagging = null, + RemoveBg = null, + }; + + Assert.Null(model.AIAutoDescription); + Assert.False(model.RawData.ContainsKey("ai-auto-description")); + Assert.Null(model.AITasks); + Assert.False(model.RawData.ContainsKey("ai-tasks")); + Assert.Null(model.AwsAutoTagging); + Assert.False(model.RawData.ContainsKey("aws-auto-tagging")); + Assert.Null(model.GoogleAutoTagging); + Assert.False(model.RawData.ContainsKey("google-auto-tagging")); + Assert.Null(model.RemoveBg); + Assert.False(model.RawData.ContainsKey("remove-bg")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Files::ExtensionStatus + { + // Null should be interpreted as omitted for these properties + AIAutoDescription = null, + AITasks = null, + AwsAutoTagging = null, + GoogleAutoTagging = null, + RemoveBg = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Files::ExtensionStatus + { + AIAutoDescription = Files::AIAutoDescription.Success, + AITasks = Files::AITasks.Success, + AwsAutoTagging = Files::AwsAutoTagging.Success, + GoogleAutoTagging = Files::GoogleAutoTagging.Success, + RemoveBg = Files::RemoveBg.Success, + }; + + Files::ExtensionStatus copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class AIAutoDescriptionTest : TestBase +{ + [Theory] + [InlineData(Files::AIAutoDescription.Success)] + [InlineData(Files::AIAutoDescription.Pending)] + [InlineData(Files::AIAutoDescription.Failed)] + public void Validation_Works(Files::AIAutoDescription rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Files::AIAutoDescription.Success)] + [InlineData(Files::AIAutoDescription.Pending)] + [InlineData(Files::AIAutoDescription.Failed)] + public void SerializationRoundtrip_Works(Files::AIAutoDescription rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class AITasksTest : TestBase +{ + [Theory] + [InlineData(Files::AITasks.Success)] + [InlineData(Files::AITasks.Pending)] + [InlineData(Files::AITasks.Failed)] + public void Validation_Works(Files::AITasks rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Files::AITasks.Success)] + [InlineData(Files::AITasks.Pending)] + [InlineData(Files::AITasks.Failed)] + public void SerializationRoundtrip_Works(Files::AITasks rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class AwsAutoTaggingTest : TestBase +{ + [Theory] + [InlineData(Files::AwsAutoTagging.Success)] + [InlineData(Files::AwsAutoTagging.Pending)] + [InlineData(Files::AwsAutoTagging.Failed)] + public void Validation_Works(Files::AwsAutoTagging rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Files::AwsAutoTagging.Success)] + [InlineData(Files::AwsAutoTagging.Pending)] + [InlineData(Files::AwsAutoTagging.Failed)] + public void SerializationRoundtrip_Works(Files::AwsAutoTagging rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class GoogleAutoTaggingTest : TestBase +{ + [Theory] + [InlineData(Files::GoogleAutoTagging.Success)] + [InlineData(Files::GoogleAutoTagging.Pending)] + [InlineData(Files::GoogleAutoTagging.Failed)] + public void Validation_Works(Files::GoogleAutoTagging rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Files::GoogleAutoTagging.Success)] + [InlineData(Files::GoogleAutoTagging.Pending)] + [InlineData(Files::GoogleAutoTagging.Failed)] + public void SerializationRoundtrip_Works(Files::GoogleAutoTagging rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class RemoveBgTest : TestBase +{ + [Theory] + [InlineData(Files::RemoveBg.Success)] + [InlineData(Files::RemoveBg.Pending)] + [InlineData(Files::RemoveBg.Failed)] + public void Validation_Works(Files::RemoveBg rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Files::RemoveBg.Success)] + [InlineData(Files::RemoveBg.Pending)] + [InlineData(Files::RemoveBg.Failed)] + public void SerializationRoundtrip_Works(Files::RemoveBg rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/Files/FileUploadParamsTest.cs b/src/Imagekit.Tests/Models/Files/FileUploadParamsTest.cs new file mode 100644 index 00000000..452e8d79 --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/FileUploadParamsTest.cs @@ -0,0 +1,1229 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Files; +using Models = Imagekit.Models; + +namespace Imagekit.Tests.Models.Files; + +public class FileUploadParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + BinaryContent file = Encoding.UTF8.GetBytes("Example data"); + + var parameters = new FileUploadParams + { + File = file, + FileName = "fileName", + Token = "token", + Checks = "\"request.folder\" : \"marketing/\"\n", + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "brand", JsonSerializer.SerializeToElement("bar") }, + { "color", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "Running shoes", + Expire = 0, + Extensions = + [ + new Models::ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new Models::ExtensionItemAutoTaggingExtension() + { + MaxTags = 5, + MinConfidence = 95, + Name = Models::ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new Models::ExtensionItemAIAutoDescription(), + new Models::ExtensionItemAITasks( + [ + new Models::ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + new Models::ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }, + ] + ), + new Models::SavedExtension("ext_abc123"), + ], + Folder = "folder", + IsPrivateFile = true, + IsPublished = true, + OverwriteAITags = true, + OverwriteCustomMetadata = true, + OverwriteFile = true, + OverwriteTags = true, + PublicKey = "publicKey", + ResponseFields = + [ + ResponseField.Tags, + ResponseField.CustomCoordinates, + ResponseField.IsPrivateFile, + ], + Signature = "signature", + Tags = ["t-shirt", "round-neck", "men"], + Transformation = new() + { + Post = + [ + new Thumbnail() { Value = "w-150,h-150" }, + new Abs() { Protocol = Protocol.Dash, Value = "sr-240_360_480_720_1080" }, + ], + Pre = "w-300,h-300,q-80", + }, + UseUniqueFileName = true, + WebhookUrl = "https://example.com", + }; + + BinaryContent expectedFile = file; + string expectedFileName = "fileName"; + string expectedToken = "token"; + string expectedChecks = "\"request.folder\" : \"marketing/\"\n"; + string expectedCustomCoordinates = "customCoordinates"; + Dictionary expectedCustomMetadata = new() + { + { "brand", JsonSerializer.SerializeToElement("bar") }, + { "color", JsonSerializer.SerializeToElement("bar") }, + }; + string expectedDescription = "Running shoes"; + long expectedExpire = 0; + List expectedExtensions = + [ + new Models::ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new Models::ExtensionItemAutoTaggingExtension() + { + MaxTags = 5, + MinConfidence = 95, + Name = Models::ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new Models::ExtensionItemAIAutoDescription(), + new Models::ExtensionItemAITasks( + [ + new Models::ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + new Models::ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }, + ] + ), + new Models::SavedExtension("ext_abc123"), + ]; + string expectedFolder = "folder"; + bool expectedIsPrivateFile = true; + bool expectedIsPublished = true; + bool expectedOverwriteAITags = true; + bool expectedOverwriteCustomMetadata = true; + bool expectedOverwriteFile = true; + bool expectedOverwriteTags = true; + string expectedPublicKey = "publicKey"; + List> expectedResponseFields = + [ + ResponseField.Tags, + ResponseField.CustomCoordinates, + ResponseField.IsPrivateFile, + ]; + string expectedSignature = "signature"; + List expectedTags = ["t-shirt", "round-neck", "men"]; + Transformation expectedTransformation = new() + { + Post = + [ + new Thumbnail() { Value = "w-150,h-150" }, + new Abs() { Protocol = Protocol.Dash, Value = "sr-240_360_480_720_1080" }, + ], + Pre = "w-300,h-300,q-80", + }; + bool expectedUseUniqueFileName = true; + string expectedWebhookUrl = "https://example.com"; + + Assert.Equal(expectedFile, parameters.File); + Assert.Equal(expectedFileName, parameters.FileName); + Assert.Equal(expectedToken, parameters.Token); + Assert.Equal(expectedChecks, parameters.Checks); + Assert.Equal(expectedCustomCoordinates, parameters.CustomCoordinates); + Assert.NotNull(parameters.CustomMetadata); + Assert.Equal(expectedCustomMetadata.Count, parameters.CustomMetadata.Count); + foreach (var item in expectedCustomMetadata) + { + Assert.True(parameters.CustomMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, parameters.CustomMetadata[item.Key])); + } + Assert.Equal(expectedDescription, parameters.Description); + Assert.Equal(expectedExpire, parameters.Expire); + Assert.NotNull(parameters.Extensions); + Assert.Equal(expectedExtensions.Count, parameters.Extensions.Count); + for (int i = 0; i < expectedExtensions.Count; i++) + { + Assert.Equal(expectedExtensions[i], parameters.Extensions[i]); + } + Assert.Equal(expectedFolder, parameters.Folder); + Assert.Equal(expectedIsPrivateFile, parameters.IsPrivateFile); + Assert.Equal(expectedIsPublished, parameters.IsPublished); + Assert.Equal(expectedOverwriteAITags, parameters.OverwriteAITags); + Assert.Equal(expectedOverwriteCustomMetadata, parameters.OverwriteCustomMetadata); + Assert.Equal(expectedOverwriteFile, parameters.OverwriteFile); + Assert.Equal(expectedOverwriteTags, parameters.OverwriteTags); + Assert.Equal(expectedPublicKey, parameters.PublicKey); + Assert.NotNull(parameters.ResponseFields); + Assert.Equal(expectedResponseFields.Count, parameters.ResponseFields.Count); + for (int i = 0; i < expectedResponseFields.Count; i++) + { + Assert.Equal(expectedResponseFields[i], parameters.ResponseFields[i]); + } + Assert.Equal(expectedSignature, parameters.Signature); + Assert.NotNull(parameters.Tags); + Assert.Equal(expectedTags.Count, parameters.Tags.Count); + for (int i = 0; i < expectedTags.Count; i++) + { + Assert.Equal(expectedTags[i], parameters.Tags[i]); + } + Assert.Equal(expectedTransformation, parameters.Transformation); + Assert.Equal(expectedUseUniqueFileName, parameters.UseUniqueFileName); + Assert.Equal(expectedWebhookUrl, parameters.WebhookUrl); + } + + [Fact] + public void OptionalNonNullableParamsUnsetAreNotSet_Works() + { + BinaryContent file = Encoding.UTF8.GetBytes("Example data"); + + var parameters = new FileUploadParams { File = file, FileName = "fileName" }; + + Assert.Null(parameters.Token); + Assert.False(parameters.RawBodyData.ContainsKey("token")); + Assert.Null(parameters.Checks); + Assert.False(parameters.RawBodyData.ContainsKey("checks")); + Assert.Null(parameters.CustomCoordinates); + Assert.False(parameters.RawBodyData.ContainsKey("customCoordinates")); + Assert.Null(parameters.CustomMetadata); + Assert.False(parameters.RawBodyData.ContainsKey("customMetadata")); + Assert.Null(parameters.Description); + Assert.False(parameters.RawBodyData.ContainsKey("description")); + Assert.Null(parameters.Expire); + Assert.False(parameters.RawBodyData.ContainsKey("expire")); + Assert.Null(parameters.Extensions); + Assert.False(parameters.RawBodyData.ContainsKey("extensions")); + Assert.Null(parameters.Folder); + Assert.False(parameters.RawBodyData.ContainsKey("folder")); + Assert.Null(parameters.IsPrivateFile); + Assert.False(parameters.RawBodyData.ContainsKey("isPrivateFile")); + Assert.Null(parameters.IsPublished); + Assert.False(parameters.RawBodyData.ContainsKey("isPublished")); + Assert.Null(parameters.OverwriteAITags); + Assert.False(parameters.RawBodyData.ContainsKey("overwriteAITags")); + Assert.Null(parameters.OverwriteCustomMetadata); + Assert.False(parameters.RawBodyData.ContainsKey("overwriteCustomMetadata")); + Assert.Null(parameters.OverwriteFile); + Assert.False(parameters.RawBodyData.ContainsKey("overwriteFile")); + Assert.Null(parameters.OverwriteTags); + Assert.False(parameters.RawBodyData.ContainsKey("overwriteTags")); + Assert.Null(parameters.PublicKey); + Assert.False(parameters.RawBodyData.ContainsKey("publicKey")); + Assert.Null(parameters.ResponseFields); + Assert.False(parameters.RawBodyData.ContainsKey("responseFields")); + Assert.Null(parameters.Signature); + Assert.False(parameters.RawBodyData.ContainsKey("signature")); + Assert.Null(parameters.Tags); + Assert.False(parameters.RawBodyData.ContainsKey("tags")); + Assert.Null(parameters.Transformation); + Assert.False(parameters.RawBodyData.ContainsKey("transformation")); + Assert.Null(parameters.UseUniqueFileName); + Assert.False(parameters.RawBodyData.ContainsKey("useUniqueFileName")); + Assert.Null(parameters.WebhookUrl); + Assert.False(parameters.RawBodyData.ContainsKey("webhookUrl")); + } + + [Fact] + public void OptionalNonNullableParamsSetToNullAreNotSet_Works() + { + BinaryContent file = Encoding.UTF8.GetBytes("Example data"); + + var parameters = new FileUploadParams + { + File = file, + FileName = "fileName", + + // Null should be interpreted as omitted for these properties + Token = null, + Checks = null, + CustomCoordinates = null, + CustomMetadata = null, + Description = null, + Expire = null, + Extensions = null, + Folder = null, + IsPrivateFile = null, + IsPublished = null, + OverwriteAITags = null, + OverwriteCustomMetadata = null, + OverwriteFile = null, + OverwriteTags = null, + PublicKey = null, + ResponseFields = null, + Signature = null, + Tags = null, + Transformation = null, + UseUniqueFileName = null, + WebhookUrl = null, + }; + + Assert.Null(parameters.Token); + Assert.False(parameters.RawBodyData.ContainsKey("token")); + Assert.Null(parameters.Checks); + Assert.False(parameters.RawBodyData.ContainsKey("checks")); + Assert.Null(parameters.CustomCoordinates); + Assert.False(parameters.RawBodyData.ContainsKey("customCoordinates")); + Assert.Null(parameters.CustomMetadata); + Assert.False(parameters.RawBodyData.ContainsKey("customMetadata")); + Assert.Null(parameters.Description); + Assert.False(parameters.RawBodyData.ContainsKey("description")); + Assert.Null(parameters.Expire); + Assert.False(parameters.RawBodyData.ContainsKey("expire")); + Assert.Null(parameters.Extensions); + Assert.False(parameters.RawBodyData.ContainsKey("extensions")); + Assert.Null(parameters.Folder); + Assert.False(parameters.RawBodyData.ContainsKey("folder")); + Assert.Null(parameters.IsPrivateFile); + Assert.False(parameters.RawBodyData.ContainsKey("isPrivateFile")); + Assert.Null(parameters.IsPublished); + Assert.False(parameters.RawBodyData.ContainsKey("isPublished")); + Assert.Null(parameters.OverwriteAITags); + Assert.False(parameters.RawBodyData.ContainsKey("overwriteAITags")); + Assert.Null(parameters.OverwriteCustomMetadata); + Assert.False(parameters.RawBodyData.ContainsKey("overwriteCustomMetadata")); + Assert.Null(parameters.OverwriteFile); + Assert.False(parameters.RawBodyData.ContainsKey("overwriteFile")); + Assert.Null(parameters.OverwriteTags); + Assert.False(parameters.RawBodyData.ContainsKey("overwriteTags")); + Assert.Null(parameters.PublicKey); + Assert.False(parameters.RawBodyData.ContainsKey("publicKey")); + Assert.Null(parameters.ResponseFields); + Assert.False(parameters.RawBodyData.ContainsKey("responseFields")); + Assert.Null(parameters.Signature); + Assert.False(parameters.RawBodyData.ContainsKey("signature")); + Assert.Null(parameters.Tags); + Assert.False(parameters.RawBodyData.ContainsKey("tags")); + Assert.Null(parameters.Transformation); + Assert.False(parameters.RawBodyData.ContainsKey("transformation")); + Assert.Null(parameters.UseUniqueFileName); + Assert.False(parameters.RawBodyData.ContainsKey("useUniqueFileName")); + Assert.Null(parameters.WebhookUrl); + Assert.False(parameters.RawBodyData.ContainsKey("webhookUrl")); + } + + [Fact] + public void Url_Works() + { + FileUploadParams parameters = new() + { + File = Encoding.UTF8.GetBytes("Example data"), + FileName = "fileName", + }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/api/v1/files/upload"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new FileUploadParams + { + File = Encoding.UTF8.GetBytes("Example data"), + FileName = "fileName", + Token = "token", + Checks = "\"request.folder\" : \"marketing/\"\n", + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "brand", JsonSerializer.SerializeToElement("bar") }, + { "color", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "Running shoes", + Expire = 0, + Extensions = + [ + new Models::ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new Models::ExtensionItemAutoTaggingExtension() + { + MaxTags = 5, + MinConfidence = 95, + Name = Models::ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new Models::ExtensionItemAIAutoDescription(), + new Models::ExtensionItemAITasks( + [ + new Models::ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + new Models::ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }, + ] + ), + new Models::SavedExtension("ext_abc123"), + ], + Folder = "folder", + IsPrivateFile = true, + IsPublished = true, + OverwriteAITags = true, + OverwriteCustomMetadata = true, + OverwriteFile = true, + OverwriteTags = true, + PublicKey = "publicKey", + ResponseFields = + [ + ResponseField.Tags, + ResponseField.CustomCoordinates, + ResponseField.IsPrivateFile, + ], + Signature = "signature", + Tags = ["t-shirt", "round-neck", "men"], + Transformation = new() + { + Post = + [ + new Thumbnail() { Value = "w-150,h-150" }, + new Abs() { Protocol = Protocol.Dash, Value = "sr-240_360_480_720_1080" }, + ], + Pre = "w-300,h-300,q-80", + }, + UseUniqueFileName = true, + WebhookUrl = "https://example.com", + }; + + FileUploadParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} + +public class ResponseFieldTest : TestBase +{ + [Theory] + [InlineData(ResponseField.Tags)] + [InlineData(ResponseField.CustomCoordinates)] + [InlineData(ResponseField.IsPrivateFile)] + [InlineData(ResponseField.EmbeddedMetadata)] + [InlineData(ResponseField.IsPublished)] + [InlineData(ResponseField.CustomMetadata)] + [InlineData(ResponseField.Metadata)] + [InlineData(ResponseField.SelectedFieldsSchema)] + public void Validation_Works(ResponseField rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(ResponseField.Tags)] + [InlineData(ResponseField.CustomCoordinates)] + [InlineData(ResponseField.IsPrivateFile)] + [InlineData(ResponseField.EmbeddedMetadata)] + [InlineData(ResponseField.IsPublished)] + [InlineData(ResponseField.CustomMetadata)] + [InlineData(ResponseField.Metadata)] + [InlineData(ResponseField.SelectedFieldsSchema)] + public void SerializationRoundtrip_Works(ResponseField rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class TransformationTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Transformation + { + Post = + [ + new Thumbnail() { Value = "w-150,h-150" }, + new Abs() { Protocol = Protocol.Dash, Value = "sr-240_360_480_720_1080" }, + ], + Pre = "w-300,h-300,q-80", + }; + + List expectedPost = + [ + new Thumbnail() { Value = "w-150,h-150" }, + new Abs() { Protocol = Protocol.Dash, Value = "sr-240_360_480_720_1080" }, + ]; + string expectedPre = "w-300,h-300,q-80"; + + Assert.NotNull(model.Post); + Assert.Equal(expectedPost.Count, model.Post.Count); + for (int i = 0; i < expectedPost.Count; i++) + { + Assert.Equal(expectedPost[i], model.Post[i]); + } + Assert.Equal(expectedPre, model.Pre); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Transformation + { + Post = + [ + new Thumbnail() { Value = "w-150,h-150" }, + new Abs() { Protocol = Protocol.Dash, Value = "sr-240_360_480_720_1080" }, + ], + Pre = "w-300,h-300,q-80", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Transformation + { + Post = + [ + new Thumbnail() { Value = "w-150,h-150" }, + new Abs() { Protocol = Protocol.Dash, Value = "sr-240_360_480_720_1080" }, + ], + Pre = "w-300,h-300,q-80", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + List expectedPost = + [ + new Thumbnail() { Value = "w-150,h-150" }, + new Abs() { Protocol = Protocol.Dash, Value = "sr-240_360_480_720_1080" }, + ]; + string expectedPre = "w-300,h-300,q-80"; + + Assert.NotNull(deserialized.Post); + Assert.Equal(expectedPost.Count, deserialized.Post.Count); + for (int i = 0; i < expectedPost.Count; i++) + { + Assert.Equal(expectedPost[i], deserialized.Post[i]); + } + Assert.Equal(expectedPre, deserialized.Pre); + } + + [Fact] + public void Validation_Works() + { + var model = new Transformation + { + Post = + [ + new Thumbnail() { Value = "w-150,h-150" }, + new Abs() { Protocol = Protocol.Dash, Value = "sr-240_360_480_720_1080" }, + ], + Pre = "w-300,h-300,q-80", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Transformation { }; + + Assert.Null(model.Post); + Assert.False(model.RawData.ContainsKey("post")); + Assert.Null(model.Pre); + Assert.False(model.RawData.ContainsKey("pre")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Transformation { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Transformation + { + // Null should be interpreted as omitted for these properties + Post = null, + Pre = null, + }; + + Assert.Null(model.Post); + Assert.False(model.RawData.ContainsKey("post")); + Assert.Null(model.Pre); + Assert.False(model.RawData.ContainsKey("pre")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Transformation + { + // Null should be interpreted as omitted for these properties + Post = null, + Pre = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Transformation + { + Post = + [ + new Thumbnail() { Value = "w-150,h-150" }, + new Abs() { Protocol = Protocol.Dash, Value = "sr-240_360_480_720_1080" }, + ], + Pre = "w-300,h-300,q-80", + }; + + Transformation copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class PostTest : TestBase +{ + [Fact] + public void TransformationValidationWorks() + { + Post value = new PostTransformation("w-400,h-400,q-70"); + value.Validate(); + } + + [Fact] + public void GifToVideoValidationWorks() + { + Post value = new GifToVideo() { Value = "q-90" }; + value.Validate(); + } + + [Fact] + public void ThumbnailValidationWorks() + { + Post value = new Thumbnail() { Value = "w-150,h-150" }; + value.Validate(); + } + + [Fact] + public void AbsValidationWorks() + { + Post value = new Abs() { Protocol = Protocol.Hls, Value = "sr-240_360_480_720_1080" }; + value.Validate(); + } + + [Fact] + public void TransformationSerializationRoundtripWorks() + { + Post value = new PostTransformation("w-400,h-400,q-70"); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void GifToVideoSerializationRoundtripWorks() + { + Post value = new GifToVideo() { Value = "q-90" }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void ThumbnailSerializationRoundtripWorks() + { + Post value = new Thumbnail() { Value = "w-150,h-150" }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void AbsSerializationRoundtripWorks() + { + Post value = new Abs() { Protocol = Protocol.Hls, Value = "sr-240_360_480_720_1080" }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class PostTransformationTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new PostTransformation { ValueValue = "w-400,h-400,q-70" }; + + JsonElement expectedType = JsonSerializer.SerializeToElement("transformation"); + string expectedValueValue = "w-400,h-400,q-70"; + + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedValueValue, model.ValueValue); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new PostTransformation { ValueValue = "w-400,h-400,q-70" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new PostTransformation { ValueValue = "w-400,h-400,q-70" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + JsonElement expectedType = JsonSerializer.SerializeToElement("transformation"); + string expectedValueValue = "w-400,h-400,q-70"; + + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedValueValue, deserialized.ValueValue); + } + + [Fact] + public void Validation_Works() + { + var model = new PostTransformation { ValueValue = "w-400,h-400,q-70" }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new PostTransformation { ValueValue = "w-400,h-400,q-70" }; + + PostTransformation copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class GifToVideoTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new GifToVideo { Value = "q-90" }; + + JsonElement expectedType = JsonSerializer.SerializeToElement("gif-to-video"); + string expectedValue = "q-90"; + + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedValue, model.Value); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new GifToVideo { Value = "q-90" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new GifToVideo { Value = "q-90" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + JsonElement expectedType = JsonSerializer.SerializeToElement("gif-to-video"); + string expectedValue = "q-90"; + + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedValue, deserialized.Value); + } + + [Fact] + public void Validation_Works() + { + var model = new GifToVideo { Value = "q-90" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new GifToVideo { }; + + Assert.Null(model.Value); + Assert.False(model.RawData.ContainsKey("value")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new GifToVideo { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new GifToVideo + { + // Null should be interpreted as omitted for these properties + Value = null, + }; + + Assert.Null(model.Value); + Assert.False(model.RawData.ContainsKey("value")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new GifToVideo + { + // Null should be interpreted as omitted for these properties + Value = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new GifToVideo { Value = "q-90" }; + + GifToVideo copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ThumbnailTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Thumbnail { Value = "w-150,h-150" }; + + JsonElement expectedType = JsonSerializer.SerializeToElement("thumbnail"); + string expectedValue = "w-150,h-150"; + + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedValue, model.Value); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Thumbnail { Value = "w-150,h-150" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Thumbnail { Value = "w-150,h-150" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + JsonElement expectedType = JsonSerializer.SerializeToElement("thumbnail"); + string expectedValue = "w-150,h-150"; + + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedValue, deserialized.Value); + } + + [Fact] + public void Validation_Works() + { + var model = new Thumbnail { Value = "w-150,h-150" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Thumbnail { }; + + Assert.Null(model.Value); + Assert.False(model.RawData.ContainsKey("value")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Thumbnail { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Thumbnail + { + // Null should be interpreted as omitted for these properties + Value = null, + }; + + Assert.Null(model.Value); + Assert.False(model.RawData.ContainsKey("value")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Thumbnail + { + // Null should be interpreted as omitted for these properties + Value = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Thumbnail { Value = "w-150,h-150" }; + + Thumbnail copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class AbsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Abs { Protocol = Protocol.Hls, Value = "sr-240_360_480_720_1080" }; + + ApiEnum expectedProtocol = Protocol.Hls; + JsonElement expectedType = JsonSerializer.SerializeToElement("abs"); + string expectedValue = "sr-240_360_480_720_1080"; + + Assert.Equal(expectedProtocol, model.Protocol); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedValue, model.Value); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Abs { Protocol = Protocol.Hls, Value = "sr-240_360_480_720_1080" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Abs { Protocol = Protocol.Hls, Value = "sr-240_360_480_720_1080" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + Assert.NotNull(deserialized); + + ApiEnum expectedProtocol = Protocol.Hls; + JsonElement expectedType = JsonSerializer.SerializeToElement("abs"); + string expectedValue = "sr-240_360_480_720_1080"; + + Assert.Equal(expectedProtocol, deserialized.Protocol); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedValue, deserialized.Value); + } + + [Fact] + public void Validation_Works() + { + var model = new Abs { Protocol = Protocol.Hls, Value = "sr-240_360_480_720_1080" }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Abs { Protocol = Protocol.Hls, Value = "sr-240_360_480_720_1080" }; + + Abs copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ProtocolTest : TestBase +{ + [Theory] + [InlineData(Protocol.Hls)] + [InlineData(Protocol.Dash)] + public void Validation_Works(Protocol rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Protocol.Hls)] + [InlineData(Protocol.Dash)] + public void SerializationRoundtrip_Works(Protocol rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/Files/FileUploadResponseTest.cs b/src/Imagekit.Tests/Models/Files/FileUploadResponseTest.cs new file mode 100644 index 00000000..a5109dd9 --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/FileUploadResponseTest.cs @@ -0,0 +1,2583 @@ +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models; +using Files = Imagekit.Models.Files; + +namespace Imagekit.Tests.Models.Files; + +public class FileUploadResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Files::FileUploadResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = + Files::FileUploadResponseExtensionStatusAIAutoDescription.Success, + AITasks = Files::FileUploadResponseExtensionStatusAITasks.Success, + AwsAutoTagging = Files::FileUploadResponseExtensionStatusAwsAutoTagging.Success, + GoogleAutoTagging = + Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Success, + RemoveBg = Files::FileUploadResponseExtensionStatusRemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + List expectedAITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ]; + string expectedAudioCodec = "audioCodec"; + long expectedBitRate = 0; + string expectedCustomCoordinates = "customCoordinates"; + Dictionary expectedCustomMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + string expectedDescription = "description"; + long expectedDuration = 0; + Dictionary expectedEmbeddedMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + Files::FileUploadResponseExtensionStatus expectedExtensionStatus = new() + { + AIAutoDescription = Files::FileUploadResponseExtensionStatusAIAutoDescription.Success, + AITasks = Files::FileUploadResponseExtensionStatusAITasks.Success, + AwsAutoTagging = Files::FileUploadResponseExtensionStatusAwsAutoTagging.Success, + GoogleAutoTagging = Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Success, + RemoveBg = Files::FileUploadResponseExtensionStatusRemoveBg.Success, + }; + string expectedFileID = "fileId"; + string expectedFilePath = "filePath"; + string expectedFileType = "fileType"; + double expectedHeight = 0; + bool expectedIsPrivateFile = true; + bool expectedIsPublished = true; + Files::FileMetadata expectedMetadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }; + string expectedName = "name"; + Dictionary expectedSelectedFieldsSchema = new() + { + { + "foo", + new() + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }; + double expectedSize = 0; + List expectedTags = ["string"]; + string expectedThumbnailUrl = "thumbnailUrl"; + string expectedUrl = "url"; + VersionInfo expectedVersionInfo = new() { ID = "id", Name = "name" }; + string expectedVideoCodec = "videoCodec"; + double expectedWidth = 0; + + Assert.NotNull(model.AITags); + Assert.Equal(expectedAITags.Count, model.AITags.Count); + for (int i = 0; i < expectedAITags.Count; i++) + { + Assert.Equal(expectedAITags[i], model.AITags[i]); + } + Assert.Equal(expectedAudioCodec, model.AudioCodec); + Assert.Equal(expectedBitRate, model.BitRate); + Assert.Equal(expectedCustomCoordinates, model.CustomCoordinates); + Assert.NotNull(model.CustomMetadata); + Assert.Equal(expectedCustomMetadata.Count, model.CustomMetadata.Count); + foreach (var item in expectedCustomMetadata) + { + Assert.True(model.CustomMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, model.CustomMetadata[item.Key])); + } + Assert.Equal(expectedDescription, model.Description); + Assert.Equal(expectedDuration, model.Duration); + Assert.NotNull(model.EmbeddedMetadata); + Assert.Equal(expectedEmbeddedMetadata.Count, model.EmbeddedMetadata.Count); + foreach (var item in expectedEmbeddedMetadata) + { + Assert.True(model.EmbeddedMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, model.EmbeddedMetadata[item.Key])); + } + Assert.Equal(expectedExtensionStatus, model.ExtensionStatus); + Assert.Equal(expectedFileID, model.FileID); + Assert.Equal(expectedFilePath, model.FilePath); + Assert.Equal(expectedFileType, model.FileType); + Assert.Equal(expectedHeight, model.Height); + Assert.Equal(expectedIsPrivateFile, model.IsPrivateFile); + Assert.Equal(expectedIsPublished, model.IsPublished); + Assert.Equal(expectedMetadata, model.Metadata); + Assert.Equal(expectedName, model.Name); + Assert.NotNull(model.SelectedFieldsSchema); + Assert.Equal(expectedSelectedFieldsSchema.Count, model.SelectedFieldsSchema.Count); + foreach (var item in expectedSelectedFieldsSchema) + { + Assert.True(model.SelectedFieldsSchema.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.SelectedFieldsSchema[item.Key]); + } + Assert.Equal(expectedSize, model.Size); + Assert.NotNull(model.Tags); + Assert.Equal(expectedTags.Count, model.Tags.Count); + for (int i = 0; i < expectedTags.Count; i++) + { + Assert.Equal(expectedTags[i], model.Tags[i]); + } + Assert.Equal(expectedThumbnailUrl, model.ThumbnailUrl); + Assert.Equal(expectedUrl, model.Url); + Assert.Equal(expectedVersionInfo, model.VersionInfo); + Assert.Equal(expectedVideoCodec, model.VideoCodec); + Assert.Equal(expectedWidth, model.Width); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Files::FileUploadResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = + Files::FileUploadResponseExtensionStatusAIAutoDescription.Success, + AITasks = Files::FileUploadResponseExtensionStatusAITasks.Success, + AwsAutoTagging = Files::FileUploadResponseExtensionStatusAwsAutoTagging.Success, + GoogleAutoTagging = + Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Success, + RemoveBg = Files::FileUploadResponseExtensionStatusRemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Files::FileUploadResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = + Files::FileUploadResponseExtensionStatusAIAutoDescription.Success, + AITasks = Files::FileUploadResponseExtensionStatusAITasks.Success, + AwsAutoTagging = Files::FileUploadResponseExtensionStatusAwsAutoTagging.Success, + GoogleAutoTagging = + Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Success, + RemoveBg = Files::FileUploadResponseExtensionStatusRemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + List expectedAITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ]; + string expectedAudioCodec = "audioCodec"; + long expectedBitRate = 0; + string expectedCustomCoordinates = "customCoordinates"; + Dictionary expectedCustomMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + string expectedDescription = "description"; + long expectedDuration = 0; + Dictionary expectedEmbeddedMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + Files::FileUploadResponseExtensionStatus expectedExtensionStatus = new() + { + AIAutoDescription = Files::FileUploadResponseExtensionStatusAIAutoDescription.Success, + AITasks = Files::FileUploadResponseExtensionStatusAITasks.Success, + AwsAutoTagging = Files::FileUploadResponseExtensionStatusAwsAutoTagging.Success, + GoogleAutoTagging = Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Success, + RemoveBg = Files::FileUploadResponseExtensionStatusRemoveBg.Success, + }; + string expectedFileID = "fileId"; + string expectedFilePath = "filePath"; + string expectedFileType = "fileType"; + double expectedHeight = 0; + bool expectedIsPrivateFile = true; + bool expectedIsPublished = true; + Files::FileMetadata expectedMetadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }; + string expectedName = "name"; + Dictionary expectedSelectedFieldsSchema = new() + { + { + "foo", + new() + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }; + double expectedSize = 0; + List expectedTags = ["string"]; + string expectedThumbnailUrl = "thumbnailUrl"; + string expectedUrl = "url"; + VersionInfo expectedVersionInfo = new() { ID = "id", Name = "name" }; + string expectedVideoCodec = "videoCodec"; + double expectedWidth = 0; + + Assert.NotNull(deserialized.AITags); + Assert.Equal(expectedAITags.Count, deserialized.AITags.Count); + for (int i = 0; i < expectedAITags.Count; i++) + { + Assert.Equal(expectedAITags[i], deserialized.AITags[i]); + } + Assert.Equal(expectedAudioCodec, deserialized.AudioCodec); + Assert.Equal(expectedBitRate, deserialized.BitRate); + Assert.Equal(expectedCustomCoordinates, deserialized.CustomCoordinates); + Assert.NotNull(deserialized.CustomMetadata); + Assert.Equal(expectedCustomMetadata.Count, deserialized.CustomMetadata.Count); + foreach (var item in expectedCustomMetadata) + { + Assert.True(deserialized.CustomMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, deserialized.CustomMetadata[item.Key])); + } + Assert.Equal(expectedDescription, deserialized.Description); + Assert.Equal(expectedDuration, deserialized.Duration); + Assert.NotNull(deserialized.EmbeddedMetadata); + Assert.Equal(expectedEmbeddedMetadata.Count, deserialized.EmbeddedMetadata.Count); + foreach (var item in expectedEmbeddedMetadata) + { + Assert.True(deserialized.EmbeddedMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, deserialized.EmbeddedMetadata[item.Key])); + } + Assert.Equal(expectedExtensionStatus, deserialized.ExtensionStatus); + Assert.Equal(expectedFileID, deserialized.FileID); + Assert.Equal(expectedFilePath, deserialized.FilePath); + Assert.Equal(expectedFileType, deserialized.FileType); + Assert.Equal(expectedHeight, deserialized.Height); + Assert.Equal(expectedIsPrivateFile, deserialized.IsPrivateFile); + Assert.Equal(expectedIsPublished, deserialized.IsPublished); + Assert.Equal(expectedMetadata, deserialized.Metadata); + Assert.Equal(expectedName, deserialized.Name); + Assert.NotNull(deserialized.SelectedFieldsSchema); + Assert.Equal(expectedSelectedFieldsSchema.Count, deserialized.SelectedFieldsSchema.Count); + foreach (var item in expectedSelectedFieldsSchema) + { + Assert.True(deserialized.SelectedFieldsSchema.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.SelectedFieldsSchema[item.Key]); + } + Assert.Equal(expectedSize, deserialized.Size); + Assert.NotNull(deserialized.Tags); + Assert.Equal(expectedTags.Count, deserialized.Tags.Count); + for (int i = 0; i < expectedTags.Count; i++) + { + Assert.Equal(expectedTags[i], deserialized.Tags[i]); + } + Assert.Equal(expectedThumbnailUrl, deserialized.ThumbnailUrl); + Assert.Equal(expectedUrl, deserialized.Url); + Assert.Equal(expectedVersionInfo, deserialized.VersionInfo); + Assert.Equal(expectedVideoCodec, deserialized.VideoCodec); + Assert.Equal(expectedWidth, deserialized.Width); + } + + [Fact] + public void Validation_Works() + { + var model = new Files::FileUploadResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = + Files::FileUploadResponseExtensionStatusAIAutoDescription.Success, + AITasks = Files::FileUploadResponseExtensionStatusAITasks.Success, + AwsAutoTagging = Files::FileUploadResponseExtensionStatusAwsAutoTagging.Success, + GoogleAutoTagging = + Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Success, + RemoveBg = Files::FileUploadResponseExtensionStatusRemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Files::FileUploadResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + CustomCoordinates = "customCoordinates", + Tags = ["string"], + }; + + Assert.Null(model.AudioCodec); + Assert.False(model.RawData.ContainsKey("audioCodec")); + Assert.Null(model.BitRate); + Assert.False(model.RawData.ContainsKey("bitRate")); + Assert.Null(model.CustomMetadata); + Assert.False(model.RawData.ContainsKey("customMetadata")); + Assert.Null(model.Description); + Assert.False(model.RawData.ContainsKey("description")); + Assert.Null(model.Duration); + Assert.False(model.RawData.ContainsKey("duration")); + Assert.Null(model.EmbeddedMetadata); + Assert.False(model.RawData.ContainsKey("embeddedMetadata")); + Assert.Null(model.ExtensionStatus); + Assert.False(model.RawData.ContainsKey("extensionStatus")); + Assert.Null(model.FileID); + Assert.False(model.RawData.ContainsKey("fileId")); + Assert.Null(model.FilePath); + Assert.False(model.RawData.ContainsKey("filePath")); + Assert.Null(model.FileType); + Assert.False(model.RawData.ContainsKey("fileType")); + Assert.Null(model.Height); + Assert.False(model.RawData.ContainsKey("height")); + Assert.Null(model.IsPrivateFile); + Assert.False(model.RawData.ContainsKey("isPrivateFile")); + Assert.Null(model.IsPublished); + Assert.False(model.RawData.ContainsKey("isPublished")); + Assert.Null(model.Metadata); + Assert.False(model.RawData.ContainsKey("metadata")); + Assert.Null(model.Name); + Assert.False(model.RawData.ContainsKey("name")); + Assert.Null(model.SelectedFieldsSchema); + Assert.False(model.RawData.ContainsKey("selectedFieldsSchema")); + Assert.Null(model.Size); + Assert.False(model.RawData.ContainsKey("size")); + Assert.Null(model.ThumbnailUrl); + Assert.False(model.RawData.ContainsKey("thumbnailUrl")); + Assert.Null(model.Url); + Assert.False(model.RawData.ContainsKey("url")); + Assert.Null(model.VersionInfo); + Assert.False(model.RawData.ContainsKey("versionInfo")); + Assert.Null(model.VideoCodec); + Assert.False(model.RawData.ContainsKey("videoCodec")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Files::FileUploadResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + CustomCoordinates = "customCoordinates", + Tags = ["string"], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Files::FileUploadResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + CustomCoordinates = "customCoordinates", + Tags = ["string"], + + // Null should be interpreted as omitted for these properties + AudioCodec = null, + BitRate = null, + CustomMetadata = null, + Description = null, + Duration = null, + EmbeddedMetadata = null, + ExtensionStatus = null, + FileID = null, + FilePath = null, + FileType = null, + Height = null, + IsPrivateFile = null, + IsPublished = null, + Metadata = null, + Name = null, + SelectedFieldsSchema = null, + Size = null, + ThumbnailUrl = null, + Url = null, + VersionInfo = null, + VideoCodec = null, + Width = null, + }; + + Assert.Null(model.AudioCodec); + Assert.False(model.RawData.ContainsKey("audioCodec")); + Assert.Null(model.BitRate); + Assert.False(model.RawData.ContainsKey("bitRate")); + Assert.Null(model.CustomMetadata); + Assert.False(model.RawData.ContainsKey("customMetadata")); + Assert.Null(model.Description); + Assert.False(model.RawData.ContainsKey("description")); + Assert.Null(model.Duration); + Assert.False(model.RawData.ContainsKey("duration")); + Assert.Null(model.EmbeddedMetadata); + Assert.False(model.RawData.ContainsKey("embeddedMetadata")); + Assert.Null(model.ExtensionStatus); + Assert.False(model.RawData.ContainsKey("extensionStatus")); + Assert.Null(model.FileID); + Assert.False(model.RawData.ContainsKey("fileId")); + Assert.Null(model.FilePath); + Assert.False(model.RawData.ContainsKey("filePath")); + Assert.Null(model.FileType); + Assert.False(model.RawData.ContainsKey("fileType")); + Assert.Null(model.Height); + Assert.False(model.RawData.ContainsKey("height")); + Assert.Null(model.IsPrivateFile); + Assert.False(model.RawData.ContainsKey("isPrivateFile")); + Assert.Null(model.IsPublished); + Assert.False(model.RawData.ContainsKey("isPublished")); + Assert.Null(model.Metadata); + Assert.False(model.RawData.ContainsKey("metadata")); + Assert.Null(model.Name); + Assert.False(model.RawData.ContainsKey("name")); + Assert.Null(model.SelectedFieldsSchema); + Assert.False(model.RawData.ContainsKey("selectedFieldsSchema")); + Assert.Null(model.Size); + Assert.False(model.RawData.ContainsKey("size")); + Assert.Null(model.ThumbnailUrl); + Assert.False(model.RawData.ContainsKey("thumbnailUrl")); + Assert.Null(model.Url); + Assert.False(model.RawData.ContainsKey("url")); + Assert.Null(model.VersionInfo); + Assert.False(model.RawData.ContainsKey("versionInfo")); + Assert.Null(model.VideoCodec); + Assert.False(model.RawData.ContainsKey("videoCodec")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Files::FileUploadResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + CustomCoordinates = "customCoordinates", + Tags = ["string"], + + // Null should be interpreted as omitted for these properties + AudioCodec = null, + BitRate = null, + CustomMetadata = null, + Description = null, + Duration = null, + EmbeddedMetadata = null, + ExtensionStatus = null, + FileID = null, + FilePath = null, + FileType = null, + Height = null, + IsPrivateFile = null, + IsPublished = null, + Metadata = null, + Name = null, + SelectedFieldsSchema = null, + Size = null, + ThumbnailUrl = null, + Url = null, + VersionInfo = null, + VideoCodec = null, + Width = null, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Files::FileUploadResponse + { + AudioCodec = "audioCodec", + BitRate = 0, + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = + Files::FileUploadResponseExtensionStatusAIAutoDescription.Success, + AITasks = Files::FileUploadResponseExtensionStatusAITasks.Success, + AwsAutoTagging = Files::FileUploadResponseExtensionStatusAwsAutoTagging.Success, + GoogleAutoTagging = + Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Success, + RemoveBg = Files::FileUploadResponseExtensionStatusRemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + Assert.Null(model.AITags); + Assert.False(model.RawData.ContainsKey("AITags")); + Assert.Null(model.CustomCoordinates); + Assert.False(model.RawData.ContainsKey("customCoordinates")); + Assert.Null(model.Tags); + Assert.False(model.RawData.ContainsKey("tags")); + } + + [Fact] + public void OptionalNullablePropertiesUnsetValidation_Works() + { + var model = new Files::FileUploadResponse + { + AudioCodec = "audioCodec", + BitRate = 0, + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = + Files::FileUploadResponseExtensionStatusAIAutoDescription.Success, + AITasks = Files::FileUploadResponseExtensionStatusAITasks.Success, + AwsAutoTagging = Files::FileUploadResponseExtensionStatusAwsAutoTagging.Success, + GoogleAutoTagging = + Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Success, + RemoveBg = Files::FileUploadResponseExtensionStatusRemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNullablePropertiesSetToNullAreSetToNull_Works() + { + var model = new Files::FileUploadResponse + { + AudioCodec = "audioCodec", + BitRate = 0, + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = + Files::FileUploadResponseExtensionStatusAIAutoDescription.Success, + AITasks = Files::FileUploadResponseExtensionStatusAITasks.Success, + AwsAutoTagging = Files::FileUploadResponseExtensionStatusAwsAutoTagging.Success, + GoogleAutoTagging = + Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Success, + RemoveBg = Files::FileUploadResponseExtensionStatusRemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + + AITags = null, + CustomCoordinates = null, + Tags = null, + }; + + Assert.Null(model.AITags); + Assert.True(model.RawData.ContainsKey("AITags")); + Assert.Null(model.CustomCoordinates); + Assert.True(model.RawData.ContainsKey("customCoordinates")); + Assert.Null(model.Tags); + Assert.True(model.RawData.ContainsKey("tags")); + } + + [Fact] + public void OptionalNullablePropertiesSetToNullValidation_Works() + { + var model = new Files::FileUploadResponse + { + AudioCodec = "audioCodec", + BitRate = 0, + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = + Files::FileUploadResponseExtensionStatusAIAutoDescription.Success, + AITasks = Files::FileUploadResponseExtensionStatusAITasks.Success, + AwsAutoTagging = Files::FileUploadResponseExtensionStatusAwsAutoTagging.Success, + GoogleAutoTagging = + Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Success, + RemoveBg = Files::FileUploadResponseExtensionStatusRemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + + AITags = null, + CustomCoordinates = null, + Tags = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Files::FileUploadResponse + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = + Files::FileUploadResponseExtensionStatusAIAutoDescription.Success, + AITasks = Files::FileUploadResponseExtensionStatusAITasks.Success, + AwsAutoTagging = Files::FileUploadResponseExtensionStatusAwsAutoTagging.Success, + GoogleAutoTagging = + Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Success, + RemoveBg = Files::FileUploadResponseExtensionStatusRemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + Files::FileUploadResponse copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class FileUploadResponseExtensionStatusTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Files::FileUploadResponseExtensionStatus + { + AIAutoDescription = Files::FileUploadResponseExtensionStatusAIAutoDescription.Success, + AITasks = Files::FileUploadResponseExtensionStatusAITasks.Success, + AwsAutoTagging = Files::FileUploadResponseExtensionStatusAwsAutoTagging.Success, + GoogleAutoTagging = Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Success, + RemoveBg = Files::FileUploadResponseExtensionStatusRemoveBg.Success, + }; + + ApiEnum< + string, + Files::FileUploadResponseExtensionStatusAIAutoDescription + > expectedAIAutoDescription = + Files::FileUploadResponseExtensionStatusAIAutoDescription.Success; + ApiEnum expectedAITasks = + Files::FileUploadResponseExtensionStatusAITasks.Success; + ApiEnum< + string, + Files::FileUploadResponseExtensionStatusAwsAutoTagging + > expectedAwsAutoTagging = Files::FileUploadResponseExtensionStatusAwsAutoTagging.Success; + ApiEnum< + string, + Files::FileUploadResponseExtensionStatusGoogleAutoTagging + > expectedGoogleAutoTagging = + Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Success; + ApiEnum expectedRemoveBg = + Files::FileUploadResponseExtensionStatusRemoveBg.Success; + + Assert.Equal(expectedAIAutoDescription, model.AIAutoDescription); + Assert.Equal(expectedAITasks, model.AITasks); + Assert.Equal(expectedAwsAutoTagging, model.AwsAutoTagging); + Assert.Equal(expectedGoogleAutoTagging, model.GoogleAutoTagging); + Assert.Equal(expectedRemoveBg, model.RemoveBg); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Files::FileUploadResponseExtensionStatus + { + AIAutoDescription = Files::FileUploadResponseExtensionStatusAIAutoDescription.Success, + AITasks = Files::FileUploadResponseExtensionStatusAITasks.Success, + AwsAutoTagging = Files::FileUploadResponseExtensionStatusAwsAutoTagging.Success, + GoogleAutoTagging = Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Success, + RemoveBg = Files::FileUploadResponseExtensionStatusRemoveBg.Success, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Files::FileUploadResponseExtensionStatus + { + AIAutoDescription = Files::FileUploadResponseExtensionStatusAIAutoDescription.Success, + AITasks = Files::FileUploadResponseExtensionStatusAITasks.Success, + AwsAutoTagging = Files::FileUploadResponseExtensionStatusAwsAutoTagging.Success, + GoogleAutoTagging = Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Success, + RemoveBg = Files::FileUploadResponseExtensionStatusRemoveBg.Success, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum< + string, + Files::FileUploadResponseExtensionStatusAIAutoDescription + > expectedAIAutoDescription = + Files::FileUploadResponseExtensionStatusAIAutoDescription.Success; + ApiEnum expectedAITasks = + Files::FileUploadResponseExtensionStatusAITasks.Success; + ApiEnum< + string, + Files::FileUploadResponseExtensionStatusAwsAutoTagging + > expectedAwsAutoTagging = Files::FileUploadResponseExtensionStatusAwsAutoTagging.Success; + ApiEnum< + string, + Files::FileUploadResponseExtensionStatusGoogleAutoTagging + > expectedGoogleAutoTagging = + Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Success; + ApiEnum expectedRemoveBg = + Files::FileUploadResponseExtensionStatusRemoveBg.Success; + + Assert.Equal(expectedAIAutoDescription, deserialized.AIAutoDescription); + Assert.Equal(expectedAITasks, deserialized.AITasks); + Assert.Equal(expectedAwsAutoTagging, deserialized.AwsAutoTagging); + Assert.Equal(expectedGoogleAutoTagging, deserialized.GoogleAutoTagging); + Assert.Equal(expectedRemoveBg, deserialized.RemoveBg); + } + + [Fact] + public void Validation_Works() + { + var model = new Files::FileUploadResponseExtensionStatus + { + AIAutoDescription = Files::FileUploadResponseExtensionStatusAIAutoDescription.Success, + AITasks = Files::FileUploadResponseExtensionStatusAITasks.Success, + AwsAutoTagging = Files::FileUploadResponseExtensionStatusAwsAutoTagging.Success, + GoogleAutoTagging = Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Success, + RemoveBg = Files::FileUploadResponseExtensionStatusRemoveBg.Success, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Files::FileUploadResponseExtensionStatus { }; + + Assert.Null(model.AIAutoDescription); + Assert.False(model.RawData.ContainsKey("ai-auto-description")); + Assert.Null(model.AITasks); + Assert.False(model.RawData.ContainsKey("ai-tasks")); + Assert.Null(model.AwsAutoTagging); + Assert.False(model.RawData.ContainsKey("aws-auto-tagging")); + Assert.Null(model.GoogleAutoTagging); + Assert.False(model.RawData.ContainsKey("google-auto-tagging")); + Assert.Null(model.RemoveBg); + Assert.False(model.RawData.ContainsKey("remove-bg")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Files::FileUploadResponseExtensionStatus { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Files::FileUploadResponseExtensionStatus + { + // Null should be interpreted as omitted for these properties + AIAutoDescription = null, + AITasks = null, + AwsAutoTagging = null, + GoogleAutoTagging = null, + RemoveBg = null, + }; + + Assert.Null(model.AIAutoDescription); + Assert.False(model.RawData.ContainsKey("ai-auto-description")); + Assert.Null(model.AITasks); + Assert.False(model.RawData.ContainsKey("ai-tasks")); + Assert.Null(model.AwsAutoTagging); + Assert.False(model.RawData.ContainsKey("aws-auto-tagging")); + Assert.Null(model.GoogleAutoTagging); + Assert.False(model.RawData.ContainsKey("google-auto-tagging")); + Assert.Null(model.RemoveBg); + Assert.False(model.RawData.ContainsKey("remove-bg")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Files::FileUploadResponseExtensionStatus + { + // Null should be interpreted as omitted for these properties + AIAutoDescription = null, + AITasks = null, + AwsAutoTagging = null, + GoogleAutoTagging = null, + RemoveBg = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Files::FileUploadResponseExtensionStatus + { + AIAutoDescription = Files::FileUploadResponseExtensionStatusAIAutoDescription.Success, + AITasks = Files::FileUploadResponseExtensionStatusAITasks.Success, + AwsAutoTagging = Files::FileUploadResponseExtensionStatusAwsAutoTagging.Success, + GoogleAutoTagging = Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Success, + RemoveBg = Files::FileUploadResponseExtensionStatusRemoveBg.Success, + }; + + Files::FileUploadResponseExtensionStatus copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class FileUploadResponseExtensionStatusAIAutoDescriptionTest : TestBase +{ + [Theory] + [InlineData(Files::FileUploadResponseExtensionStatusAIAutoDescription.Success)] + [InlineData(Files::FileUploadResponseExtensionStatusAIAutoDescription.Pending)] + [InlineData(Files::FileUploadResponseExtensionStatusAIAutoDescription.Failed)] + public void Validation_Works(Files::FileUploadResponseExtensionStatusAIAutoDescription rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Files::FileUploadResponseExtensionStatusAIAutoDescription.Success)] + [InlineData(Files::FileUploadResponseExtensionStatusAIAutoDescription.Pending)] + [InlineData(Files::FileUploadResponseExtensionStatusAIAutoDescription.Failed)] + public void SerializationRoundtrip_Works( + Files::FileUploadResponseExtensionStatusAIAutoDescription rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class FileUploadResponseExtensionStatusAITasksTest : TestBase +{ + [Theory] + [InlineData(Files::FileUploadResponseExtensionStatusAITasks.Success)] + [InlineData(Files::FileUploadResponseExtensionStatusAITasks.Pending)] + [InlineData(Files::FileUploadResponseExtensionStatusAITasks.Failed)] + public void Validation_Works(Files::FileUploadResponseExtensionStatusAITasks rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Files::FileUploadResponseExtensionStatusAITasks.Success)] + [InlineData(Files::FileUploadResponseExtensionStatusAITasks.Pending)] + [InlineData(Files::FileUploadResponseExtensionStatusAITasks.Failed)] + public void SerializationRoundtrip_Works( + Files::FileUploadResponseExtensionStatusAITasks rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class FileUploadResponseExtensionStatusAwsAutoTaggingTest : TestBase +{ + [Theory] + [InlineData(Files::FileUploadResponseExtensionStatusAwsAutoTagging.Success)] + [InlineData(Files::FileUploadResponseExtensionStatusAwsAutoTagging.Pending)] + [InlineData(Files::FileUploadResponseExtensionStatusAwsAutoTagging.Failed)] + public void Validation_Works(Files::FileUploadResponseExtensionStatusAwsAutoTagging rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Files::FileUploadResponseExtensionStatusAwsAutoTagging.Success)] + [InlineData(Files::FileUploadResponseExtensionStatusAwsAutoTagging.Pending)] + [InlineData(Files::FileUploadResponseExtensionStatusAwsAutoTagging.Failed)] + public void SerializationRoundtrip_Works( + Files::FileUploadResponseExtensionStatusAwsAutoTagging rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class FileUploadResponseExtensionStatusGoogleAutoTaggingTest : TestBase +{ + [Theory] + [InlineData(Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Success)] + [InlineData(Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Pending)] + [InlineData(Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Failed)] + public void Validation_Works(Files::FileUploadResponseExtensionStatusGoogleAutoTagging rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Success)] + [InlineData(Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Pending)] + [InlineData(Files::FileUploadResponseExtensionStatusGoogleAutoTagging.Failed)] + public void SerializationRoundtrip_Works( + Files::FileUploadResponseExtensionStatusGoogleAutoTagging rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class FileUploadResponseExtensionStatusRemoveBgTest : TestBase +{ + [Theory] + [InlineData(Files::FileUploadResponseExtensionStatusRemoveBg.Success)] + [InlineData(Files::FileUploadResponseExtensionStatusRemoveBg.Pending)] + [InlineData(Files::FileUploadResponseExtensionStatusRemoveBg.Failed)] + public void Validation_Works(Files::FileUploadResponseExtensionStatusRemoveBg rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Files::FileUploadResponseExtensionStatusRemoveBg.Success)] + [InlineData(Files::FileUploadResponseExtensionStatusRemoveBg.Pending)] + [InlineData(Files::FileUploadResponseExtensionStatusRemoveBg.Failed)] + public void SerializationRoundtrip_Works( + Files::FileUploadResponseExtensionStatusRemoveBg rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/Files/FolderTest.cs b/src/Imagekit.Tests/Models/Files/FolderTest.cs new file mode 100644 index 00000000..895d7435 --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/FolderTest.cs @@ -0,0 +1,302 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Files; + +namespace Imagekit.Tests.Models.Files; + +public class FolderTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Folder + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FolderID = "folderId", + FolderPath = "folderPath", + Name = "name", + Type = FolderType.Folder, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + }; + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + Dictionary expectedCustomMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + string expectedFolderID = "folderId"; + string expectedFolderPath = "folderPath"; + string expectedName = "name"; + ApiEnum expectedType = FolderType.Folder; + DateTimeOffset expectedUpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.NotNull(model.CustomMetadata); + Assert.Equal(expectedCustomMetadata.Count, model.CustomMetadata.Count); + foreach (var item in expectedCustomMetadata) + { + Assert.True(model.CustomMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, model.CustomMetadata[item.Key])); + } + Assert.Equal(expectedFolderID, model.FolderID); + Assert.Equal(expectedFolderPath, model.FolderPath); + Assert.Equal(expectedName, model.Name); + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedUpdatedAt, model.UpdatedAt); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Folder + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FolderID = "folderId", + FolderPath = "folderPath", + Name = "name", + Type = FolderType.Folder, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Folder + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FolderID = "folderId", + FolderPath = "folderPath", + Name = "name", + Type = FolderType.Folder, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + Assert.NotNull(deserialized); + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + Dictionary expectedCustomMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + string expectedFolderID = "folderId"; + string expectedFolderPath = "folderPath"; + string expectedName = "name"; + ApiEnum expectedType = FolderType.Folder; + DateTimeOffset expectedUpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.NotNull(deserialized.CustomMetadata); + Assert.Equal(expectedCustomMetadata.Count, deserialized.CustomMetadata.Count); + foreach (var item in expectedCustomMetadata) + { + Assert.True(deserialized.CustomMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, deserialized.CustomMetadata[item.Key])); + } + Assert.Equal(expectedFolderID, deserialized.FolderID); + Assert.Equal(expectedFolderPath, deserialized.FolderPath); + Assert.Equal(expectedName, deserialized.Name); + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedUpdatedAt, deserialized.UpdatedAt); + } + + [Fact] + public void Validation_Works() + { + var model = new Folder + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FolderID = "folderId", + FolderPath = "folderPath", + Name = "name", + Type = FolderType.Folder, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Folder { }; + + Assert.Null(model.CreatedAt); + Assert.False(model.RawData.ContainsKey("createdAt")); + Assert.Null(model.CustomMetadata); + Assert.False(model.RawData.ContainsKey("customMetadata")); + Assert.Null(model.FolderID); + Assert.False(model.RawData.ContainsKey("folderId")); + Assert.Null(model.FolderPath); + Assert.False(model.RawData.ContainsKey("folderPath")); + Assert.Null(model.Name); + Assert.False(model.RawData.ContainsKey("name")); + Assert.Null(model.Type); + Assert.False(model.RawData.ContainsKey("type")); + Assert.Null(model.UpdatedAt); + Assert.False(model.RawData.ContainsKey("updatedAt")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Folder { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Folder + { + // Null should be interpreted as omitted for these properties + CreatedAt = null, + CustomMetadata = null, + FolderID = null, + FolderPath = null, + Name = null, + Type = null, + UpdatedAt = null, + }; + + Assert.Null(model.CreatedAt); + Assert.False(model.RawData.ContainsKey("createdAt")); + Assert.Null(model.CustomMetadata); + Assert.False(model.RawData.ContainsKey("customMetadata")); + Assert.Null(model.FolderID); + Assert.False(model.RawData.ContainsKey("folderId")); + Assert.Null(model.FolderPath); + Assert.False(model.RawData.ContainsKey("folderPath")); + Assert.Null(model.Name); + Assert.False(model.RawData.ContainsKey("name")); + Assert.Null(model.Type); + Assert.False(model.RawData.ContainsKey("type")); + Assert.Null(model.UpdatedAt); + Assert.False(model.RawData.ContainsKey("updatedAt")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Folder + { + // Null should be interpreted as omitted for these properties + CreatedAt = null, + CustomMetadata = null, + FolderID = null, + FolderPath = null, + Name = null, + Type = null, + UpdatedAt = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Folder + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FolderID = "folderId", + FolderPath = "folderPath", + Name = "name", + Type = FolderType.Folder, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + }; + + Folder copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class FolderTypeTest : TestBase +{ + [Theory] + [InlineData(FolderType.Folder)] + public void Validation_Works(FolderType rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(FolderType.Folder)] + public void SerializationRoundtrip_Works(FolderType rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/Files/Metadata/MetadataGetFromUrlParamsTest.cs b/src/Imagekit.Tests/Models/Files/Metadata/MetadataGetFromUrlParamsTest.cs new file mode 100644 index 00000000..d314a12e --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/Metadata/MetadataGetFromUrlParamsTest.cs @@ -0,0 +1,42 @@ +using System; +using Imagekit.Models.Files.Metadata; + +namespace Imagekit.Tests.Models.Files.Metadata; + +public class MetadataGetFromUrlParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new MetadataGetFromUrlParams { UrlValue = "https://example.com" }; + + string expectedUrlValue = "https://example.com"; + + Assert.Equal(expectedUrlValue, parameters.UrlValue); + } + + [Fact] + public void Url_Works() + { + MetadataGetFromUrlParams parameters = new() { UrlValue = "https://example.com" }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual( + new Uri("https://api.imagekit.io/v1/metadata?url=https%3a%2f%2fexample.com"), + url + ) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new MetadataGetFromUrlParams { UrlValue = "https://example.com" }; + + MetadataGetFromUrlParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/Metadata/MetadataGetParamsTest.cs b/src/Imagekit.Tests/Models/Files/Metadata/MetadataGetParamsTest.cs new file mode 100644 index 00000000..4fd20ba8 --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/Metadata/MetadataGetParamsTest.cs @@ -0,0 +1,39 @@ +using System; +using Imagekit.Models.Files.Metadata; + +namespace Imagekit.Tests.Models.Files.Metadata; + +public class MetadataGetParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new MetadataGetParams { FileID = "fileId" }; + + string expectedFileID = "fileId"; + + Assert.Equal(expectedFileID, parameters.FileID); + } + + [Fact] + public void Url_Works() + { + MetadataGetParams parameters = new() { FileID = "fileId" }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/files/fileId/metadata"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new MetadataGetParams { FileID = "fileId" }; + + MetadataGetParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/UpdateFileRequestTest.cs b/src/Imagekit.Tests/Models/Files/UpdateFileRequestTest.cs new file mode 100644 index 00000000..6686a8a1 --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/UpdateFileRequestTest.cs @@ -0,0 +1,1500 @@ +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models; +using Imagekit.Models.Files; + +namespace Imagekit.Tests.Models.Files; + +public class UpdateFileRequestTest : TestBase +{ + [Fact] + public void DetailsValidationWorks() + { + UpdateFileRequest value = new UpdateFileDetails() + { + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Extensions = + [ + new ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 5, + MinConfidence = 95, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new ExtensionItemAIAutoDescription(), + new ExtensionItemAITasks( + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + new ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }, + ] + ), + new SavedExtension("ext_abc123"), + ], + RemoveAITags = new All(), + Tags = ["tag1", "tag2"], + WebhookUrl = "https://example.com", + }; + value.Validate(); + } + + [Fact] + public void ChangePublicationStatusValidationWorks() + { + UpdateFileRequest value = new ChangePublicationStatus() + { + Publish = new() { IsPublished = true, IncludeFileVersions = true }, + }; + value.Validate(); + } + + [Fact] + public void DetailsSerializationRoundtripWorks() + { + UpdateFileRequest value = new UpdateFileDetails() + { + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Extensions = + [ + new ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 5, + MinConfidence = 95, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new ExtensionItemAIAutoDescription(), + new ExtensionItemAITasks( + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + new ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }, + ] + ), + new SavedExtension("ext_abc123"), + ], + RemoveAITags = new All(), + Tags = ["tag1", "tag2"], + WebhookUrl = "https://example.com", + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void ChangePublicationStatusSerializationRoundtripWorks() + { + UpdateFileRequest value = new ChangePublicationStatus() + { + Publish = new() { IsPublished = true, IncludeFileVersions = true }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class UpdateFileDetailsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new UpdateFileDetails + { + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Extensions = + [ + new ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 5, + MinConfidence = 95, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new ExtensionItemAIAutoDescription(), + new ExtensionItemAITasks( + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + new ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }, + ] + ), + new SavedExtension("ext_abc123"), + ], + RemoveAITags = new All(), + Tags = ["tag1", "tag2"], + WebhookUrl = "https://example.com", + }; + + string expectedCustomCoordinates = "customCoordinates"; + Dictionary expectedCustomMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + string expectedDescription = "description"; + List expectedExtensions = + [ + new ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 5, + MinConfidence = 95, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new ExtensionItemAIAutoDescription(), + new ExtensionItemAITasks( + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + new ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }, + ] + ), + new SavedExtension("ext_abc123"), + ]; + RemoveAITags expectedRemoveAITags = new All(); + List expectedTags = ["tag1", "tag2"]; + string expectedWebhookUrl = "https://example.com"; + + Assert.Equal(expectedCustomCoordinates, model.CustomCoordinates); + Assert.NotNull(model.CustomMetadata); + Assert.Equal(expectedCustomMetadata.Count, model.CustomMetadata.Count); + foreach (var item in expectedCustomMetadata) + { + Assert.True(model.CustomMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, model.CustomMetadata[item.Key])); + } + Assert.Equal(expectedDescription, model.Description); + Assert.NotNull(model.Extensions); + Assert.Equal(expectedExtensions.Count, model.Extensions.Count); + for (int i = 0; i < expectedExtensions.Count; i++) + { + Assert.Equal(expectedExtensions[i], model.Extensions[i]); + } + Assert.Equal(expectedRemoveAITags, model.RemoveAITags); + Assert.NotNull(model.Tags); + Assert.Equal(expectedTags.Count, model.Tags.Count); + for (int i = 0; i < expectedTags.Count; i++) + { + Assert.Equal(expectedTags[i], model.Tags[i]); + } + Assert.Equal(expectedWebhookUrl, model.WebhookUrl); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new UpdateFileDetails + { + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Extensions = + [ + new ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 5, + MinConfidence = 95, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new ExtensionItemAIAutoDescription(), + new ExtensionItemAITasks( + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + new ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }, + ] + ), + new SavedExtension("ext_abc123"), + ], + RemoveAITags = new All(), + Tags = ["tag1", "tag2"], + WebhookUrl = "https://example.com", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new UpdateFileDetails + { + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Extensions = + [ + new ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 5, + MinConfidence = 95, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new ExtensionItemAIAutoDescription(), + new ExtensionItemAITasks( + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + new ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }, + ] + ), + new SavedExtension("ext_abc123"), + ], + RemoveAITags = new All(), + Tags = ["tag1", "tag2"], + WebhookUrl = "https://example.com", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedCustomCoordinates = "customCoordinates"; + Dictionary expectedCustomMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + string expectedDescription = "description"; + List expectedExtensions = + [ + new ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 5, + MinConfidence = 95, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new ExtensionItemAIAutoDescription(), + new ExtensionItemAITasks( + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + new ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }, + ] + ), + new SavedExtension("ext_abc123"), + ]; + RemoveAITags expectedRemoveAITags = new All(); + List expectedTags = ["tag1", "tag2"]; + string expectedWebhookUrl = "https://example.com"; + + Assert.Equal(expectedCustomCoordinates, deserialized.CustomCoordinates); + Assert.NotNull(deserialized.CustomMetadata); + Assert.Equal(expectedCustomMetadata.Count, deserialized.CustomMetadata.Count); + foreach (var item in expectedCustomMetadata) + { + Assert.True(deserialized.CustomMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, deserialized.CustomMetadata[item.Key])); + } + Assert.Equal(expectedDescription, deserialized.Description); + Assert.NotNull(deserialized.Extensions); + Assert.Equal(expectedExtensions.Count, deserialized.Extensions.Count); + for (int i = 0; i < expectedExtensions.Count; i++) + { + Assert.Equal(expectedExtensions[i], deserialized.Extensions[i]); + } + Assert.Equal(expectedRemoveAITags, deserialized.RemoveAITags); + Assert.NotNull(deserialized.Tags); + Assert.Equal(expectedTags.Count, deserialized.Tags.Count); + for (int i = 0; i < expectedTags.Count; i++) + { + Assert.Equal(expectedTags[i], deserialized.Tags[i]); + } + Assert.Equal(expectedWebhookUrl, deserialized.WebhookUrl); + } + + [Fact] + public void Validation_Works() + { + var model = new UpdateFileDetails + { + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Extensions = + [ + new ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 5, + MinConfidence = 95, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new ExtensionItemAIAutoDescription(), + new ExtensionItemAITasks( + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + new ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }, + ] + ), + new SavedExtension("ext_abc123"), + ], + RemoveAITags = new All(), + Tags = ["tag1", "tag2"], + WebhookUrl = "https://example.com", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new UpdateFileDetails + { + CustomCoordinates = "customCoordinates", + Tags = ["tag1", "tag2"], + }; + + Assert.Null(model.CustomMetadata); + Assert.False(model.RawData.ContainsKey("customMetadata")); + Assert.Null(model.Description); + Assert.False(model.RawData.ContainsKey("description")); + Assert.Null(model.Extensions); + Assert.False(model.RawData.ContainsKey("extensions")); + Assert.Null(model.RemoveAITags); + Assert.False(model.RawData.ContainsKey("removeAITags")); + Assert.Null(model.WebhookUrl); + Assert.False(model.RawData.ContainsKey("webhookUrl")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new UpdateFileDetails + { + CustomCoordinates = "customCoordinates", + Tags = ["tag1", "tag2"], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new UpdateFileDetails + { + CustomCoordinates = "customCoordinates", + Tags = ["tag1", "tag2"], + + // Null should be interpreted as omitted for these properties + CustomMetadata = null, + Description = null, + Extensions = null, + RemoveAITags = null, + WebhookUrl = null, + }; + + Assert.Null(model.CustomMetadata); + Assert.False(model.RawData.ContainsKey("customMetadata")); + Assert.Null(model.Description); + Assert.False(model.RawData.ContainsKey("description")); + Assert.Null(model.Extensions); + Assert.False(model.RawData.ContainsKey("extensions")); + Assert.Null(model.RemoveAITags); + Assert.False(model.RawData.ContainsKey("removeAITags")); + Assert.Null(model.WebhookUrl); + Assert.False(model.RawData.ContainsKey("webhookUrl")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new UpdateFileDetails + { + CustomCoordinates = "customCoordinates", + Tags = ["tag1", "tag2"], + + // Null should be interpreted as omitted for these properties + CustomMetadata = null, + Description = null, + Extensions = null, + RemoveAITags = null, + WebhookUrl = null, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNullablePropertiesUnsetAreNotSet_Works() + { + var model = new UpdateFileDetails + { + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Extensions = + [ + new ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 5, + MinConfidence = 95, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new ExtensionItemAIAutoDescription(), + new ExtensionItemAITasks( + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + new ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }, + ] + ), + new SavedExtension("ext_abc123"), + ], + RemoveAITags = new All(), + WebhookUrl = "https://example.com", + }; + + Assert.Null(model.CustomCoordinates); + Assert.False(model.RawData.ContainsKey("customCoordinates")); + Assert.Null(model.Tags); + Assert.False(model.RawData.ContainsKey("tags")); + } + + [Fact] + public void OptionalNullablePropertiesUnsetValidation_Works() + { + var model = new UpdateFileDetails + { + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Extensions = + [ + new ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 5, + MinConfidence = 95, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new ExtensionItemAIAutoDescription(), + new ExtensionItemAITasks( + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + new ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }, + ] + ), + new SavedExtension("ext_abc123"), + ], + RemoveAITags = new All(), + WebhookUrl = "https://example.com", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNullablePropertiesSetToNullAreSetToNull_Works() + { + var model = new UpdateFileDetails + { + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Extensions = + [ + new ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 5, + MinConfidence = 95, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new ExtensionItemAIAutoDescription(), + new ExtensionItemAITasks( + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + new ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }, + ] + ), + new SavedExtension("ext_abc123"), + ], + RemoveAITags = new All(), + WebhookUrl = "https://example.com", + + CustomCoordinates = null, + Tags = null, + }; + + Assert.Null(model.CustomCoordinates); + Assert.True(model.RawData.ContainsKey("customCoordinates")); + Assert.Null(model.Tags); + Assert.True(model.RawData.ContainsKey("tags")); + } + + [Fact] + public void OptionalNullablePropertiesSetToNullValidation_Works() + { + var model = new UpdateFileDetails + { + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Extensions = + [ + new ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 5, + MinConfidence = 95, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new ExtensionItemAIAutoDescription(), + new ExtensionItemAITasks( + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + new ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }, + ] + ), + new SavedExtension("ext_abc123"), + ], + RemoveAITags = new All(), + WebhookUrl = "https://example.com", + + CustomCoordinates = null, + Tags = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new UpdateFileDetails + { + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Extensions = + [ + new ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new ExtensionItemAutoTaggingExtension() + { + MaxTags = 5, + MinConfidence = 95, + Name = ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new ExtensionItemAIAutoDescription(), + new ExtensionItemAITasks( + [ + new ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible in this image?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "tshirt", "dress", "trousers", "jacket"], + }, + new ExtensionItemAITasksTaskYesNo() + { + Instruction = "Is this a luxury or high-end fashion item?", + OnNo = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnUnknown = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + OnYes = new() + { + AddTags = ["luxury", "premium"], + RemoveTags = ["budget", "affordable"], + SetMetadata = [new() { Field = "price_range", Value = "premium" }], + UnsetMetadata = [new("price_range")], + }, + }, + ] + ), + new SavedExtension("ext_abc123"), + ], + RemoveAITags = new All(), + Tags = ["tag1", "tag2"], + WebhookUrl = "https://example.com", + }; + + UpdateFileDetails copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class RemoveAITagsTest : TestBase +{ + [Fact] + public void StringsValidationWorks() + { + RemoveAITags value = new(["string"]); + value.Validate(); + } + + [Fact] + public void AllValidationWorks() + { + RemoveAITags value = new All(); + value.Validate(); + } + + [Fact] + public void StringsSerializationRoundtripWorks() + { + RemoveAITags value = new(["string"]); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void AllSerializationRoundtripWorks() + { + RemoveAITags value = new All(); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class AllTest : TestBase +{ + [Fact] + public void DefaultValidation_Works() + { + var constant = new All(); + constant.Validate(); + } + + [Fact] + public void ValidConstantValidation_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("all"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + constant.Validate(); + } + + [Fact] + public void InvalidConstantValidationThrows_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + Assert.Throws(() => constant.Validate()); + } + + [Fact] + public void DefaultRoundtrip_Works() + { + var constant = new All(); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void ValidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("all"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void InvalidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(constant, deserialized); + } +} + +public class ChangePublicationStatusTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ChangePublicationStatus + { + Publish = new() { IsPublished = true, IncludeFileVersions = true }, + }; + + Publish expectedPublish = new() { IsPublished = true, IncludeFileVersions = true }; + + Assert.Equal(expectedPublish, model.Publish); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ChangePublicationStatus + { + Publish = new() { IsPublished = true, IncludeFileVersions = true }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ChangePublicationStatus + { + Publish = new() { IsPublished = true, IncludeFileVersions = true }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + Publish expectedPublish = new() { IsPublished = true, IncludeFileVersions = true }; + + Assert.Equal(expectedPublish, deserialized.Publish); + } + + [Fact] + public void Validation_Works() + { + var model = new ChangePublicationStatus + { + Publish = new() { IsPublished = true, IncludeFileVersions = true }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ChangePublicationStatus { }; + + Assert.Null(model.Publish); + Assert.False(model.RawData.ContainsKey("publish")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new ChangePublicationStatus { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ChangePublicationStatus + { + // Null should be interpreted as omitted for these properties + Publish = null, + }; + + Assert.Null(model.Publish); + Assert.False(model.RawData.ContainsKey("publish")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new ChangePublicationStatus + { + // Null should be interpreted as omitted for these properties + Publish = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ChangePublicationStatus + { + Publish = new() { IsPublished = true, IncludeFileVersions = true }, + }; + + ChangePublicationStatus copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class PublishTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Publish { IsPublished = true, IncludeFileVersions = true }; + + bool expectedIsPublished = true; + bool expectedIncludeFileVersions = true; + + Assert.Equal(expectedIsPublished, model.IsPublished); + Assert.Equal(expectedIncludeFileVersions, model.IncludeFileVersions); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Publish { IsPublished = true, IncludeFileVersions = true }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Publish { IsPublished = true, IncludeFileVersions = true }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + bool expectedIsPublished = true; + bool expectedIncludeFileVersions = true; + + Assert.Equal(expectedIsPublished, deserialized.IsPublished); + Assert.Equal(expectedIncludeFileVersions, deserialized.IncludeFileVersions); + } + + [Fact] + public void Validation_Works() + { + var model = new Publish { IsPublished = true, IncludeFileVersions = true }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Publish { IsPublished = true }; + + Assert.Null(model.IncludeFileVersions); + Assert.False(model.RawData.ContainsKey("includeFileVersions")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Publish { IsPublished = true }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Publish + { + IsPublished = true, + + // Null should be interpreted as omitted for these properties + IncludeFileVersions = null, + }; + + Assert.Null(model.IncludeFileVersions); + Assert.False(model.RawData.ContainsKey("includeFileVersions")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Publish + { + IsPublished = true, + + // Null should be interpreted as omitted for these properties + IncludeFileVersions = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Publish { IsPublished = true, IncludeFileVersions = true }; + + Publish copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/Versions/VersionDeleteParamsTest.cs b/src/Imagekit.Tests/Models/Files/Versions/VersionDeleteParamsTest.cs new file mode 100644 index 00000000..a1d5549e --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/Versions/VersionDeleteParamsTest.cs @@ -0,0 +1,44 @@ +using System; +using Imagekit.Models.Files.Versions; + +namespace Imagekit.Tests.Models.Files.Versions; + +public class VersionDeleteParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new VersionDeleteParams { FileID = "fileId", VersionID = "versionId" }; + + string expectedFileID = "fileId"; + string expectedVersionID = "versionId"; + + Assert.Equal(expectedFileID, parameters.FileID); + Assert.Equal(expectedVersionID, parameters.VersionID); + } + + [Fact] + public void Url_Works() + { + VersionDeleteParams parameters = new() { FileID = "fileId", VersionID = "versionId" }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual( + new Uri("https://api.imagekit.io/v1/files/fileId/versions/versionId"), + url + ) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new VersionDeleteParams { FileID = "fileId", VersionID = "versionId" }; + + VersionDeleteParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/Versions/VersionDeleteResponseTest.cs b/src/Imagekit.Tests/Models/Files/Versions/VersionDeleteResponseTest.cs new file mode 100644 index 00000000..64c279f0 --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/Versions/VersionDeleteResponseTest.cs @@ -0,0 +1,59 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Files.Versions; + +namespace Imagekit.Tests.Models.Files.Versions; + +public class VersionDeleteResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new VersionDeleteResponse { }; + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new VersionDeleteResponse { }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new VersionDeleteResponse { }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + } + + [Fact] + public void Validation_Works() + { + var model = new VersionDeleteResponse { }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new VersionDeleteResponse { }; + + VersionDeleteResponse copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/Versions/VersionGetParamsTest.cs b/src/Imagekit.Tests/Models/Files/Versions/VersionGetParamsTest.cs new file mode 100644 index 00000000..a60ccf03 --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/Versions/VersionGetParamsTest.cs @@ -0,0 +1,44 @@ +using System; +using Imagekit.Models.Files.Versions; + +namespace Imagekit.Tests.Models.Files.Versions; + +public class VersionGetParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new VersionGetParams { FileID = "fileId", VersionID = "versionId" }; + + string expectedFileID = "fileId"; + string expectedVersionID = "versionId"; + + Assert.Equal(expectedFileID, parameters.FileID); + Assert.Equal(expectedVersionID, parameters.VersionID); + } + + [Fact] + public void Url_Works() + { + VersionGetParams parameters = new() { FileID = "fileId", VersionID = "versionId" }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual( + new Uri("https://api.imagekit.io/v1/files/fileId/versions/versionId"), + url + ) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new VersionGetParams { FileID = "fileId", VersionID = "versionId" }; + + VersionGetParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/Versions/VersionListParamsTest.cs b/src/Imagekit.Tests/Models/Files/Versions/VersionListParamsTest.cs new file mode 100644 index 00000000..4385106a --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/Versions/VersionListParamsTest.cs @@ -0,0 +1,39 @@ +using System; +using Imagekit.Models.Files.Versions; + +namespace Imagekit.Tests.Models.Files.Versions; + +public class VersionListParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new VersionListParams { FileID = "fileId" }; + + string expectedFileID = "fileId"; + + Assert.Equal(expectedFileID, parameters.FileID); + } + + [Fact] + public void Url_Works() + { + VersionListParams parameters = new() { FileID = "fileId" }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/files/fileId/versions"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new VersionListParams { FileID = "fileId" }; + + VersionListParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Files/Versions/VersionRestoreParamsTest.cs b/src/Imagekit.Tests/Models/Files/Versions/VersionRestoreParamsTest.cs new file mode 100644 index 00000000..549da7b0 --- /dev/null +++ b/src/Imagekit.Tests/Models/Files/Versions/VersionRestoreParamsTest.cs @@ -0,0 +1,44 @@ +using System; +using Imagekit.Models.Files.Versions; + +namespace Imagekit.Tests.Models.Files.Versions; + +public class VersionRestoreParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new VersionRestoreParams { FileID = "fileId", VersionID = "versionId" }; + + string expectedFileID = "fileId"; + string expectedVersionID = "versionId"; + + Assert.Equal(expectedFileID, parameters.FileID); + Assert.Equal(expectedVersionID, parameters.VersionID); + } + + [Fact] + public void Url_Works() + { + VersionRestoreParams parameters = new() { FileID = "fileId", VersionID = "versionId" }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual( + new Uri("https://api.imagekit.io/v1/files/fileId/versions/versionId/restore"), + url + ) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new VersionRestoreParams { FileID = "fileId", VersionID = "versionId" }; + + VersionRestoreParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Folders/FolderCopyParamsTest.cs b/src/Imagekit.Tests/Models/Folders/FolderCopyParamsTest.cs new file mode 100644 index 00000000..6869bc7b --- /dev/null +++ b/src/Imagekit.Tests/Models/Folders/FolderCopyParamsTest.cs @@ -0,0 +1,86 @@ +using System; +using Imagekit.Models.Folders; + +namespace Imagekit.Tests.Models.Folders; + +public class FolderCopyParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new FolderCopyParams + { + DestinationPath = "/path/of/destination/folder", + SourceFolderPath = "/path/of/source/folder", + IncludeVersions = true, + }; + + string expectedDestinationPath = "/path/of/destination/folder"; + string expectedSourceFolderPath = "/path/of/source/folder"; + bool expectedIncludeVersions = true; + + Assert.Equal(expectedDestinationPath, parameters.DestinationPath); + Assert.Equal(expectedSourceFolderPath, parameters.SourceFolderPath); + Assert.Equal(expectedIncludeVersions, parameters.IncludeVersions); + } + + [Fact] + public void OptionalNonNullableParamsUnsetAreNotSet_Works() + { + var parameters = new FolderCopyParams + { + DestinationPath = "/path/of/destination/folder", + SourceFolderPath = "/path/of/source/folder", + }; + + Assert.Null(parameters.IncludeVersions); + Assert.False(parameters.RawBodyData.ContainsKey("includeVersions")); + } + + [Fact] + public void OptionalNonNullableParamsSetToNullAreNotSet_Works() + { + var parameters = new FolderCopyParams + { + DestinationPath = "/path/of/destination/folder", + SourceFolderPath = "/path/of/source/folder", + + // Null should be interpreted as omitted for these properties + IncludeVersions = null, + }; + + Assert.Null(parameters.IncludeVersions); + Assert.False(parameters.RawBodyData.ContainsKey("includeVersions")); + } + + [Fact] + public void Url_Works() + { + FolderCopyParams parameters = new() + { + DestinationPath = "/path/of/destination/folder", + SourceFolderPath = "/path/of/source/folder", + }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/bulkJobs/copyFolder"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new FolderCopyParams + { + DestinationPath = "/path/of/destination/folder", + SourceFolderPath = "/path/of/source/folder", + IncludeVersions = true, + }; + + FolderCopyParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Folders/FolderCopyResponseTest.cs b/src/Imagekit.Tests/Models/Folders/FolderCopyResponseTest.cs new file mode 100644 index 00000000..bb6952dd --- /dev/null +++ b/src/Imagekit.Tests/Models/Folders/FolderCopyResponseTest.cs @@ -0,0 +1,67 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Folders; + +namespace Imagekit.Tests.Models.Folders; + +public class FolderCopyResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new FolderCopyResponse { JobID = "jobId" }; + + string expectedJobID = "jobId"; + + Assert.Equal(expectedJobID, model.JobID); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new FolderCopyResponse { JobID = "jobId" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new FolderCopyResponse { JobID = "jobId" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedJobID = "jobId"; + + Assert.Equal(expectedJobID, deserialized.JobID); + } + + [Fact] + public void Validation_Works() + { + var model = new FolderCopyResponse { JobID = "jobId" }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new FolderCopyResponse { JobID = "jobId" }; + + FolderCopyResponse copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Folders/FolderCreateParamsTest.cs b/src/Imagekit.Tests/Models/Folders/FolderCreateParamsTest.cs new file mode 100644 index 00000000..86c09c06 --- /dev/null +++ b/src/Imagekit.Tests/Models/Folders/FolderCreateParamsTest.cs @@ -0,0 +1,51 @@ +using System; +using Imagekit.Models.Folders; + +namespace Imagekit.Tests.Models.Folders; + +public class FolderCreateParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new FolderCreateParams + { + FolderName = "summer", + ParentFolderPath = "/product/images/", + }; + + string expectedFolderName = "summer"; + string expectedParentFolderPath = "/product/images/"; + + Assert.Equal(expectedFolderName, parameters.FolderName); + Assert.Equal(expectedParentFolderPath, parameters.ParentFolderPath); + } + + [Fact] + public void Url_Works() + { + FolderCreateParams parameters = new() + { + FolderName = "summer", + ParentFolderPath = "/product/images/", + }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True(TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/folder"), url)); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new FolderCreateParams + { + FolderName = "summer", + ParentFolderPath = "/product/images/", + }; + + FolderCreateParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Folders/FolderCreateResponseTest.cs b/src/Imagekit.Tests/Models/Folders/FolderCreateResponseTest.cs new file mode 100644 index 00000000..a358a4be --- /dev/null +++ b/src/Imagekit.Tests/Models/Folders/FolderCreateResponseTest.cs @@ -0,0 +1,59 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Folders; + +namespace Imagekit.Tests.Models.Folders; + +public class FolderCreateResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new FolderCreateResponse { }; + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new FolderCreateResponse { }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new FolderCreateResponse { }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + } + + [Fact] + public void Validation_Works() + { + var model = new FolderCreateResponse { }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new FolderCreateResponse { }; + + FolderCreateResponse copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Folders/FolderDeleteParamsTest.cs b/src/Imagekit.Tests/Models/Folders/FolderDeleteParamsTest.cs new file mode 100644 index 00000000..214fb04a --- /dev/null +++ b/src/Imagekit.Tests/Models/Folders/FolderDeleteParamsTest.cs @@ -0,0 +1,37 @@ +using System; +using Imagekit.Models.Folders; + +namespace Imagekit.Tests.Models.Folders; + +public class FolderDeleteParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new FolderDeleteParams { FolderPath = "/folder/to/delete/" }; + + string expectedFolderPath = "/folder/to/delete/"; + + Assert.Equal(expectedFolderPath, parameters.FolderPath); + } + + [Fact] + public void Url_Works() + { + FolderDeleteParams parameters = new() { FolderPath = "/folder/to/delete/" }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True(TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/folder"), url)); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new FolderDeleteParams { FolderPath = "/folder/to/delete/" }; + + FolderDeleteParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Folders/FolderDeleteResponseTest.cs b/src/Imagekit.Tests/Models/Folders/FolderDeleteResponseTest.cs new file mode 100644 index 00000000..6a92f813 --- /dev/null +++ b/src/Imagekit.Tests/Models/Folders/FolderDeleteResponseTest.cs @@ -0,0 +1,59 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Folders; + +namespace Imagekit.Tests.Models.Folders; + +public class FolderDeleteResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new FolderDeleteResponse { }; + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new FolderDeleteResponse { }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new FolderDeleteResponse { }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + } + + [Fact] + public void Validation_Works() + { + var model = new FolderDeleteResponse { }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new FolderDeleteResponse { }; + + FolderDeleteResponse copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Folders/FolderMoveParamsTest.cs b/src/Imagekit.Tests/Models/Folders/FolderMoveParamsTest.cs new file mode 100644 index 00000000..911ef4a2 --- /dev/null +++ b/src/Imagekit.Tests/Models/Folders/FolderMoveParamsTest.cs @@ -0,0 +1,53 @@ +using System; +using Imagekit.Models.Folders; + +namespace Imagekit.Tests.Models.Folders; + +public class FolderMoveParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new FolderMoveParams + { + DestinationPath = "/path/of/destination/folder", + SourceFolderPath = "/path/of/source/folder", + }; + + string expectedDestinationPath = "/path/of/destination/folder"; + string expectedSourceFolderPath = "/path/of/source/folder"; + + Assert.Equal(expectedDestinationPath, parameters.DestinationPath); + Assert.Equal(expectedSourceFolderPath, parameters.SourceFolderPath); + } + + [Fact] + public void Url_Works() + { + FolderMoveParams parameters = new() + { + DestinationPath = "/path/of/destination/folder", + SourceFolderPath = "/path/of/source/folder", + }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/bulkJobs/moveFolder"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new FolderMoveParams + { + DestinationPath = "/path/of/destination/folder", + SourceFolderPath = "/path/of/source/folder", + }; + + FolderMoveParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Folders/FolderMoveResponseTest.cs b/src/Imagekit.Tests/Models/Folders/FolderMoveResponseTest.cs new file mode 100644 index 00000000..ef446e4a --- /dev/null +++ b/src/Imagekit.Tests/Models/Folders/FolderMoveResponseTest.cs @@ -0,0 +1,67 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Folders; + +namespace Imagekit.Tests.Models.Folders; + +public class FolderMoveResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new FolderMoveResponse { JobID = "jobId" }; + + string expectedJobID = "jobId"; + + Assert.Equal(expectedJobID, model.JobID); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new FolderMoveResponse { JobID = "jobId" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new FolderMoveResponse { JobID = "jobId" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedJobID = "jobId"; + + Assert.Equal(expectedJobID, deserialized.JobID); + } + + [Fact] + public void Validation_Works() + { + var model = new FolderMoveResponse { JobID = "jobId" }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new FolderMoveResponse { JobID = "jobId" }; + + FolderMoveResponse copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Folders/FolderRenameParamsTest.cs b/src/Imagekit.Tests/Models/Folders/FolderRenameParamsTest.cs new file mode 100644 index 00000000..c4a34d4f --- /dev/null +++ b/src/Imagekit.Tests/Models/Folders/FolderRenameParamsTest.cs @@ -0,0 +1,86 @@ +using System; +using Imagekit.Models.Folders; + +namespace Imagekit.Tests.Models.Folders; + +public class FolderRenameParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new FolderRenameParams + { + FolderPath = "/path/of/folder", + NewFolderName = "new-folder-name", + PurgeCache = true, + }; + + string expectedFolderPath = "/path/of/folder"; + string expectedNewFolderName = "new-folder-name"; + bool expectedPurgeCache = true; + + Assert.Equal(expectedFolderPath, parameters.FolderPath); + Assert.Equal(expectedNewFolderName, parameters.NewFolderName); + Assert.Equal(expectedPurgeCache, parameters.PurgeCache); + } + + [Fact] + public void OptionalNonNullableParamsUnsetAreNotSet_Works() + { + var parameters = new FolderRenameParams + { + FolderPath = "/path/of/folder", + NewFolderName = "new-folder-name", + }; + + Assert.Null(parameters.PurgeCache); + Assert.False(parameters.RawBodyData.ContainsKey("purgeCache")); + } + + [Fact] + public void OptionalNonNullableParamsSetToNullAreNotSet_Works() + { + var parameters = new FolderRenameParams + { + FolderPath = "/path/of/folder", + NewFolderName = "new-folder-name", + + // Null should be interpreted as omitted for these properties + PurgeCache = null, + }; + + Assert.Null(parameters.PurgeCache); + Assert.False(parameters.RawBodyData.ContainsKey("purgeCache")); + } + + [Fact] + public void Url_Works() + { + FolderRenameParams parameters = new() + { + FolderPath = "/path/of/folder", + NewFolderName = "new-folder-name", + }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/bulkJobs/renameFolder"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new FolderRenameParams + { + FolderPath = "/path/of/folder", + NewFolderName = "new-folder-name", + PurgeCache = true, + }; + + FolderRenameParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Folders/FolderRenameResponseTest.cs b/src/Imagekit.Tests/Models/Folders/FolderRenameResponseTest.cs new file mode 100644 index 00000000..3e194afd --- /dev/null +++ b/src/Imagekit.Tests/Models/Folders/FolderRenameResponseTest.cs @@ -0,0 +1,67 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Folders; + +namespace Imagekit.Tests.Models.Folders; + +public class FolderRenameResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new FolderRenameResponse { JobID = "jobId" }; + + string expectedJobID = "jobId"; + + Assert.Equal(expectedJobID, model.JobID); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new FolderRenameResponse { JobID = "jobId" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new FolderRenameResponse { JobID = "jobId" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedJobID = "jobId"; + + Assert.Equal(expectedJobID, deserialized.JobID); + } + + [Fact] + public void Validation_Works() + { + var model = new FolderRenameResponse { JobID = "jobId" }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new FolderRenameResponse { JobID = "jobId" }; + + FolderRenameResponse copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Folders/Job/JobGetParamsTest.cs b/src/Imagekit.Tests/Models/Folders/Job/JobGetParamsTest.cs new file mode 100644 index 00000000..b1ed45a2 --- /dev/null +++ b/src/Imagekit.Tests/Models/Folders/Job/JobGetParamsTest.cs @@ -0,0 +1,37 @@ +using System; +using Imagekit.Models.Folders.Job; + +namespace Imagekit.Tests.Models.Folders.Job; + +public class JobGetParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new JobGetParams { JobID = "jobId" }; + + string expectedJobID = "jobId"; + + Assert.Equal(expectedJobID, parameters.JobID); + } + + [Fact] + public void Url_Works() + { + JobGetParams parameters = new() { JobID = "jobId" }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True(TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/bulkJobs/jobId"), url)); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new JobGetParams { JobID = "jobId" }; + + JobGetParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Folders/Job/JobGetResponseTest.cs b/src/Imagekit.Tests/Models/Folders/Job/JobGetResponseTest.cs new file mode 100644 index 00000000..b70a022b --- /dev/null +++ b/src/Imagekit.Tests/Models/Folders/Job/JobGetResponseTest.cs @@ -0,0 +1,288 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Folders.Job; + +namespace Imagekit.Tests.Models.Folders.Job; + +public class JobGetResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new JobGetResponse + { + JobID = "5d5b1a9b4c8c4c0001f3e4a2", + PurgeRequestID = "purgeRequestId", + Status = Status.Completed, + Type = Type.CopyFolder, + }; + + string expectedJobID = "5d5b1a9b4c8c4c0001f3e4a2"; + string expectedPurgeRequestID = "purgeRequestId"; + ApiEnum expectedStatus = Status.Completed; + ApiEnum expectedType = Type.CopyFolder; + + Assert.Equal(expectedJobID, model.JobID); + Assert.Equal(expectedPurgeRequestID, model.PurgeRequestID); + Assert.Equal(expectedStatus, model.Status); + Assert.Equal(expectedType, model.Type); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new JobGetResponse + { + JobID = "5d5b1a9b4c8c4c0001f3e4a2", + PurgeRequestID = "purgeRequestId", + Status = Status.Completed, + Type = Type.CopyFolder, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new JobGetResponse + { + JobID = "5d5b1a9b4c8c4c0001f3e4a2", + PurgeRequestID = "purgeRequestId", + Status = Status.Completed, + Type = Type.CopyFolder, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedJobID = "5d5b1a9b4c8c4c0001f3e4a2"; + string expectedPurgeRequestID = "purgeRequestId"; + ApiEnum expectedStatus = Status.Completed; + ApiEnum expectedType = Type.CopyFolder; + + Assert.Equal(expectedJobID, deserialized.JobID); + Assert.Equal(expectedPurgeRequestID, deserialized.PurgeRequestID); + Assert.Equal(expectedStatus, deserialized.Status); + Assert.Equal(expectedType, deserialized.Type); + } + + [Fact] + public void Validation_Works() + { + var model = new JobGetResponse + { + JobID = "5d5b1a9b4c8c4c0001f3e4a2", + PurgeRequestID = "purgeRequestId", + Status = Status.Completed, + Type = Type.CopyFolder, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new JobGetResponse { }; + + Assert.Null(model.JobID); + Assert.False(model.RawData.ContainsKey("jobId")); + Assert.Null(model.PurgeRequestID); + Assert.False(model.RawData.ContainsKey("purgeRequestId")); + Assert.Null(model.Status); + Assert.False(model.RawData.ContainsKey("status")); + Assert.Null(model.Type); + Assert.False(model.RawData.ContainsKey("type")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new JobGetResponse { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new JobGetResponse + { + // Null should be interpreted as omitted for these properties + JobID = null, + PurgeRequestID = null, + Status = null, + Type = null, + }; + + Assert.Null(model.JobID); + Assert.False(model.RawData.ContainsKey("jobId")); + Assert.Null(model.PurgeRequestID); + Assert.False(model.RawData.ContainsKey("purgeRequestId")); + Assert.Null(model.Status); + Assert.False(model.RawData.ContainsKey("status")); + Assert.Null(model.Type); + Assert.False(model.RawData.ContainsKey("type")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new JobGetResponse + { + // Null should be interpreted as omitted for these properties + JobID = null, + PurgeRequestID = null, + Status = null, + Type = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new JobGetResponse + { + JobID = "5d5b1a9b4c8c4c0001f3e4a2", + PurgeRequestID = "purgeRequestId", + Status = Status.Completed, + Type = Type.CopyFolder, + }; + + JobGetResponse copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class StatusTest : TestBase +{ + [Theory] + [InlineData(Status.Pending)] + [InlineData(Status.Completed)] + public void Validation_Works(Status rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Status.Pending)] + [InlineData(Status.Completed)] + public void SerializationRoundtrip_Works(Status rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class TypeTest : TestBase +{ + [Theory] + [InlineData(Type.CopyFolder)] + [InlineData(Type.MoveFolder)] + [InlineData(Type.RenameFolder)] + public void Validation_Works(Type rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Type.CopyFolder)] + [InlineData(Type.MoveFolder)] + [InlineData(Type.RenameFolder)] + public void SerializationRoundtrip_Works(Type rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/GetImageAttributesOptionsTest.cs b/src/Imagekit.Tests/Models/GetImageAttributesOptionsTest.cs new file mode 100644 index 00000000..255c0336 --- /dev/null +++ b/src/Imagekit.Tests/Models/GetImageAttributesOptionsTest.cs @@ -0,0 +1,1202 @@ +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class GetImageAttributesOptionsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new GetImageAttributesOptions + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + ExpiresIn = 0, + QueryParameters = new Dictionary() { { "foo", "string" } }, + Signed = true, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + TransformationPosition = TransformationPosition.Path, + DeviceBreakpoints = [640, 750, 828, 1080, 1200, 1920, 2048, 3840], + ImageBreakpoints = [16, 32, 48, 64, 96, 128, 256, 384], + Sizes = "(min-width: 768px) 50vw, 100vw", + Width = 400, + }; + + string expectedSrc = "/my-image.jpg"; + string expectedUrlEndpoint = "https://ik.imagekit.io/demo"; + double expectedExpiresIn = 0; + Dictionary expectedQueryParameters = new() { { "foo", "string" } }; + bool expectedSigned = true; + List expectedTransformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ]; + ApiEnum expectedTransformationPosition = + TransformationPosition.Path; + List expectedDeviceBreakpoints = [640, 750, 828, 1080, 1200, 1920, 2048, 3840]; + List expectedImageBreakpoints = [16, 32, 48, 64, 96, 128, 256, 384]; + string expectedSizes = "(min-width: 768px) 50vw, 100vw"; + double expectedWidth = 400; + + Assert.Equal(expectedSrc, model.Src); + Assert.Equal(expectedUrlEndpoint, model.UrlEndpoint); + Assert.Equal(expectedExpiresIn, model.ExpiresIn); + Assert.NotNull(model.QueryParameters); + Assert.Equal(expectedQueryParameters.Count, model.QueryParameters.Count); + foreach (var item in expectedQueryParameters) + { + Assert.True(model.QueryParameters.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.QueryParameters[item.Key]); + } + Assert.Equal(expectedSigned, model.Signed); + Assert.NotNull(model.Transformation); + Assert.Equal(expectedTransformation.Count, model.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], model.Transformation[i]); + } + Assert.Equal(expectedTransformationPosition, model.TransformationPosition); + Assert.NotNull(model.DeviceBreakpoints); + Assert.Equal(expectedDeviceBreakpoints.Count, model.DeviceBreakpoints.Count); + for (int i = 0; i < expectedDeviceBreakpoints.Count; i++) + { + Assert.Equal(expectedDeviceBreakpoints[i], model.DeviceBreakpoints[i]); + } + Assert.NotNull(model.ImageBreakpoints); + Assert.Equal(expectedImageBreakpoints.Count, model.ImageBreakpoints.Count); + for (int i = 0; i < expectedImageBreakpoints.Count; i++) + { + Assert.Equal(expectedImageBreakpoints[i], model.ImageBreakpoints[i]); + } + Assert.Equal(expectedSizes, model.Sizes); + Assert.Equal(expectedWidth, model.Width); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new GetImageAttributesOptions + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + ExpiresIn = 0, + QueryParameters = new Dictionary() { { "foo", "string" } }, + Signed = true, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + TransformationPosition = TransformationPosition.Path, + DeviceBreakpoints = [640, 750, 828, 1080, 1200, 1920, 2048, 3840], + ImageBreakpoints = [16, 32, 48, 64, 96, 128, 256, 384], + Sizes = "(min-width: 768px) 50vw, 100vw", + Width = 400, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new GetImageAttributesOptions + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + ExpiresIn = 0, + QueryParameters = new Dictionary() { { "foo", "string" } }, + Signed = true, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + TransformationPosition = TransformationPosition.Path, + DeviceBreakpoints = [640, 750, 828, 1080, 1200, 1920, 2048, 3840], + ImageBreakpoints = [16, 32, 48, 64, 96, 128, 256, 384], + Sizes = "(min-width: 768px) 50vw, 100vw", + Width = 400, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedSrc = "/my-image.jpg"; + string expectedUrlEndpoint = "https://ik.imagekit.io/demo"; + double expectedExpiresIn = 0; + Dictionary expectedQueryParameters = new() { { "foo", "string" } }; + bool expectedSigned = true; + List expectedTransformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ]; + ApiEnum expectedTransformationPosition = + TransformationPosition.Path; + List expectedDeviceBreakpoints = [640, 750, 828, 1080, 1200, 1920, 2048, 3840]; + List expectedImageBreakpoints = [16, 32, 48, 64, 96, 128, 256, 384]; + string expectedSizes = "(min-width: 768px) 50vw, 100vw"; + double expectedWidth = 400; + + Assert.Equal(expectedSrc, deserialized.Src); + Assert.Equal(expectedUrlEndpoint, deserialized.UrlEndpoint); + Assert.Equal(expectedExpiresIn, deserialized.ExpiresIn); + Assert.NotNull(deserialized.QueryParameters); + Assert.Equal(expectedQueryParameters.Count, deserialized.QueryParameters.Count); + foreach (var item in expectedQueryParameters) + { + Assert.True(deserialized.QueryParameters.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.QueryParameters[item.Key]); + } + Assert.Equal(expectedSigned, deserialized.Signed); + Assert.NotNull(deserialized.Transformation); + Assert.Equal(expectedTransformation.Count, deserialized.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], deserialized.Transformation[i]); + } + Assert.Equal(expectedTransformationPosition, deserialized.TransformationPosition); + Assert.NotNull(deserialized.DeviceBreakpoints); + Assert.Equal(expectedDeviceBreakpoints.Count, deserialized.DeviceBreakpoints.Count); + for (int i = 0; i < expectedDeviceBreakpoints.Count; i++) + { + Assert.Equal(expectedDeviceBreakpoints[i], deserialized.DeviceBreakpoints[i]); + } + Assert.NotNull(deserialized.ImageBreakpoints); + Assert.Equal(expectedImageBreakpoints.Count, deserialized.ImageBreakpoints.Count); + for (int i = 0; i < expectedImageBreakpoints.Count; i++) + { + Assert.Equal(expectedImageBreakpoints[i], deserialized.ImageBreakpoints[i]); + } + Assert.Equal(expectedSizes, deserialized.Sizes); + Assert.Equal(expectedWidth, deserialized.Width); + } + + [Fact] + public void Validation_Works() + { + var model = new GetImageAttributesOptions + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + ExpiresIn = 0, + QueryParameters = new Dictionary() { { "foo", "string" } }, + Signed = true, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + TransformationPosition = TransformationPosition.Path, + DeviceBreakpoints = [640, 750, 828, 1080, 1200, 1920, 2048, 3840], + ImageBreakpoints = [16, 32, 48, 64, 96, 128, 256, 384], + Sizes = "(min-width: 768px) 50vw, 100vw", + Width = 400, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new GetImageAttributesOptions + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + }; + + Assert.Null(model.ExpiresIn); + Assert.False(model.RawData.ContainsKey("expiresIn")); + Assert.Null(model.QueryParameters); + Assert.False(model.RawData.ContainsKey("queryParameters")); + Assert.Null(model.Signed); + Assert.False(model.RawData.ContainsKey("signed")); + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + Assert.Null(model.TransformationPosition); + Assert.False(model.RawData.ContainsKey("transformationPosition")); + Assert.Null(model.DeviceBreakpoints); + Assert.False(model.RawData.ContainsKey("deviceBreakpoints")); + Assert.Null(model.ImageBreakpoints); + Assert.False(model.RawData.ContainsKey("imageBreakpoints")); + Assert.Null(model.Sizes); + Assert.False(model.RawData.ContainsKey("sizes")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new GetImageAttributesOptions + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new GetImageAttributesOptions + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + + // Null should be interpreted as omitted for these properties + ExpiresIn = null, + QueryParameters = null, + Signed = null, + Transformation = null, + TransformationPosition = null, + DeviceBreakpoints = null, + ImageBreakpoints = null, + Sizes = null, + Width = null, + }; + + Assert.Null(model.ExpiresIn); + Assert.False(model.RawData.ContainsKey("expiresIn")); + Assert.Null(model.QueryParameters); + Assert.False(model.RawData.ContainsKey("queryParameters")); + Assert.Null(model.Signed); + Assert.False(model.RawData.ContainsKey("signed")); + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + Assert.Null(model.TransformationPosition); + Assert.False(model.RawData.ContainsKey("transformationPosition")); + Assert.Null(model.DeviceBreakpoints); + Assert.False(model.RawData.ContainsKey("deviceBreakpoints")); + Assert.Null(model.ImageBreakpoints); + Assert.False(model.RawData.ContainsKey("imageBreakpoints")); + Assert.Null(model.Sizes); + Assert.False(model.RawData.ContainsKey("sizes")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new GetImageAttributesOptions + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + + // Null should be interpreted as omitted for these properties + ExpiresIn = null, + QueryParameters = null, + Signed = null, + Transformation = null, + TransformationPosition = null, + DeviceBreakpoints = null, + ImageBreakpoints = null, + Sizes = null, + Width = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new GetImageAttributesOptions + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + ExpiresIn = 0, + QueryParameters = new Dictionary() { { "foo", "string" } }, + Signed = true, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + TransformationPosition = TransformationPosition.Path, + DeviceBreakpoints = [640, 750, 828, 1080, 1200, 1920, 2048, 3840], + ImageBreakpoints = [16, 32, 48, 64, 96, 128, 256, 384], + Sizes = "(min-width: 768px) 50vw, 100vw", + Width = 400, + }; + + GetImageAttributesOptions copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class GetImageAttributesOptionsGetImageAttributesOptionsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new GetImageAttributesOptionsGetImageAttributesOptions + { + DeviceBreakpoints = [640, 750, 828, 1080, 1200, 1920, 2048, 3840], + ImageBreakpoints = [16, 32, 48, 64, 96, 128, 256, 384], + Sizes = "(min-width: 768px) 50vw, 100vw", + Width = 400, + }; + + List expectedDeviceBreakpoints = [640, 750, 828, 1080, 1200, 1920, 2048, 3840]; + List expectedImageBreakpoints = [16, 32, 48, 64, 96, 128, 256, 384]; + string expectedSizes = "(min-width: 768px) 50vw, 100vw"; + double expectedWidth = 400; + + Assert.NotNull(model.DeviceBreakpoints); + Assert.Equal(expectedDeviceBreakpoints.Count, model.DeviceBreakpoints.Count); + for (int i = 0; i < expectedDeviceBreakpoints.Count; i++) + { + Assert.Equal(expectedDeviceBreakpoints[i], model.DeviceBreakpoints[i]); + } + Assert.NotNull(model.ImageBreakpoints); + Assert.Equal(expectedImageBreakpoints.Count, model.ImageBreakpoints.Count); + for (int i = 0; i < expectedImageBreakpoints.Count; i++) + { + Assert.Equal(expectedImageBreakpoints[i], model.ImageBreakpoints[i]); + } + Assert.Equal(expectedSizes, model.Sizes); + Assert.Equal(expectedWidth, model.Width); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new GetImageAttributesOptionsGetImageAttributesOptions + { + DeviceBreakpoints = [640, 750, 828, 1080, 1200, 1920, 2048, 3840], + ImageBreakpoints = [16, 32, 48, 64, 96, 128, 256, 384], + Sizes = "(min-width: 768px) 50vw, 100vw", + Width = 400, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new GetImageAttributesOptionsGetImageAttributesOptions + { + DeviceBreakpoints = [640, 750, 828, 1080, 1200, 1920, 2048, 3840], + ImageBreakpoints = [16, 32, 48, 64, 96, 128, 256, 384], + Sizes = "(min-width: 768px) 50vw, 100vw", + Width = 400, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + List expectedDeviceBreakpoints = [640, 750, 828, 1080, 1200, 1920, 2048, 3840]; + List expectedImageBreakpoints = [16, 32, 48, 64, 96, 128, 256, 384]; + string expectedSizes = "(min-width: 768px) 50vw, 100vw"; + double expectedWidth = 400; + + Assert.NotNull(deserialized.DeviceBreakpoints); + Assert.Equal(expectedDeviceBreakpoints.Count, deserialized.DeviceBreakpoints.Count); + for (int i = 0; i < expectedDeviceBreakpoints.Count; i++) + { + Assert.Equal(expectedDeviceBreakpoints[i], deserialized.DeviceBreakpoints[i]); + } + Assert.NotNull(deserialized.ImageBreakpoints); + Assert.Equal(expectedImageBreakpoints.Count, deserialized.ImageBreakpoints.Count); + for (int i = 0; i < expectedImageBreakpoints.Count; i++) + { + Assert.Equal(expectedImageBreakpoints[i], deserialized.ImageBreakpoints[i]); + } + Assert.Equal(expectedSizes, deserialized.Sizes); + Assert.Equal(expectedWidth, deserialized.Width); + } + + [Fact] + public void Validation_Works() + { + var model = new GetImageAttributesOptionsGetImageAttributesOptions + { + DeviceBreakpoints = [640, 750, 828, 1080, 1200, 1920, 2048, 3840], + ImageBreakpoints = [16, 32, 48, 64, 96, 128, 256, 384], + Sizes = "(min-width: 768px) 50vw, 100vw", + Width = 400, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new GetImageAttributesOptionsGetImageAttributesOptions { }; + + Assert.Null(model.DeviceBreakpoints); + Assert.False(model.RawData.ContainsKey("deviceBreakpoints")); + Assert.Null(model.ImageBreakpoints); + Assert.False(model.RawData.ContainsKey("imageBreakpoints")); + Assert.Null(model.Sizes); + Assert.False(model.RawData.ContainsKey("sizes")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new GetImageAttributesOptionsGetImageAttributesOptions { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new GetImageAttributesOptionsGetImageAttributesOptions + { + // Null should be interpreted as omitted for these properties + DeviceBreakpoints = null, + ImageBreakpoints = null, + Sizes = null, + Width = null, + }; + + Assert.Null(model.DeviceBreakpoints); + Assert.False(model.RawData.ContainsKey("deviceBreakpoints")); + Assert.Null(model.ImageBreakpoints); + Assert.False(model.RawData.ContainsKey("imageBreakpoints")); + Assert.Null(model.Sizes); + Assert.False(model.RawData.ContainsKey("sizes")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new GetImageAttributesOptionsGetImageAttributesOptions + { + // Null should be interpreted as omitted for these properties + DeviceBreakpoints = null, + ImageBreakpoints = null, + Sizes = null, + Width = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new GetImageAttributesOptionsGetImageAttributesOptions + { + DeviceBreakpoints = [640, 750, 828, 1080, 1200, 1920, 2048, 3840], + ImageBreakpoints = [16, 32, 48, 64, 96, 128, 256, 384], + Sizes = "(min-width: 768px) 50vw, 100vw", + Width = 400, + }; + + GetImageAttributesOptionsGetImageAttributesOptions copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/ImageOverlayTest.cs b/src/Imagekit.Tests/Models/ImageOverlayTest.cs new file mode 100644 index 00000000..de087a1d --- /dev/null +++ b/src/Imagekit.Tests/Models/ImageOverlayTest.cs @@ -0,0 +1,1911 @@ +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class ImageOverlayTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ImageOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = Encoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + + ApiEnum expectedLayerMode = LayerMode.Multiply; + OverlayPosition expectedPosition = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }; + OverlayTiming expectedTiming = new() + { + Duration = 0, + End = 0, + Start = 0, + }; + string expectedInput = "input"; + JsonElement expectedType = JsonSerializer.SerializeToElement("image"); + ApiEnum expectedEncoding = Encoding.Auto; + List expectedTransformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ]; + + Assert.Equal(expectedLayerMode, model.LayerMode); + Assert.Equal(expectedPosition, model.Position); + Assert.Equal(expectedTiming, model.Timing); + Assert.Equal(expectedInput, model.Input); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedEncoding, model.Encoding); + Assert.NotNull(model.Transformation); + Assert.Equal(expectedTransformation.Count, model.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], model.Transformation[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ImageOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = Encoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ImageOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = Encoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum expectedLayerMode = LayerMode.Multiply; + OverlayPosition expectedPosition = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }; + OverlayTiming expectedTiming = new() + { + Duration = 0, + End = 0, + Start = 0, + }; + string expectedInput = "input"; + JsonElement expectedType = JsonSerializer.SerializeToElement("image"); + ApiEnum expectedEncoding = Encoding.Auto; + List expectedTransformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ]; + + Assert.Equal(expectedLayerMode, deserialized.LayerMode); + Assert.Equal(expectedPosition, deserialized.Position); + Assert.Equal(expectedTiming, deserialized.Timing); + Assert.Equal(expectedInput, deserialized.Input); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedEncoding, deserialized.Encoding); + Assert.NotNull(deserialized.Transformation); + Assert.Equal(expectedTransformation.Count, deserialized.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], deserialized.Transformation[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new ImageOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = Encoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ImageOverlay { Input = "input" }; + + Assert.Null(model.LayerMode); + Assert.False(model.RawData.ContainsKey("layerMode")); + Assert.Null(model.Position); + Assert.False(model.RawData.ContainsKey("position")); + Assert.Null(model.Timing); + Assert.False(model.RawData.ContainsKey("timing")); + Assert.Null(model.Encoding); + Assert.False(model.RawData.ContainsKey("encoding")); + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new ImageOverlay { Input = "input" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ImageOverlay + { + Input = "input", + + // Null should be interpreted as omitted for these properties + LayerMode = null, + Position = null, + Timing = null, + Encoding = null, + Transformation = null, + }; + + Assert.Null(model.LayerMode); + Assert.False(model.RawData.ContainsKey("layerMode")); + Assert.Null(model.Position); + Assert.False(model.RawData.ContainsKey("position")); + Assert.Null(model.Timing); + Assert.False(model.RawData.ContainsKey("timing")); + Assert.Null(model.Encoding); + Assert.False(model.RawData.ContainsKey("encoding")); + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new ImageOverlay + { + Input = "input", + + // Null should be interpreted as omitted for these properties + LayerMode = null, + Position = null, + Timing = null, + Encoding = null, + Transformation = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ImageOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = Encoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + + ImageOverlay copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ImageOverlayImageOverlayTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ImageOverlayImageOverlay + { + Input = "input", + Encoding = Encoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + + string expectedInput = "input"; + JsonElement expectedType = JsonSerializer.SerializeToElement("image"); + ApiEnum expectedEncoding = Encoding.Auto; + List expectedTransformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ]; + + Assert.Equal(expectedInput, model.Input); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedEncoding, model.Encoding); + Assert.NotNull(model.Transformation); + Assert.Equal(expectedTransformation.Count, model.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], model.Transformation[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ImageOverlayImageOverlay + { + Input = "input", + Encoding = Encoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ImageOverlayImageOverlay + { + Input = "input", + Encoding = Encoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedInput = "input"; + JsonElement expectedType = JsonSerializer.SerializeToElement("image"); + ApiEnum expectedEncoding = Encoding.Auto; + List expectedTransformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ]; + + Assert.Equal(expectedInput, deserialized.Input); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedEncoding, deserialized.Encoding); + Assert.NotNull(deserialized.Transformation); + Assert.Equal(expectedTransformation.Count, deserialized.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], deserialized.Transformation[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new ImageOverlayImageOverlay + { + Input = "input", + Encoding = Encoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ImageOverlayImageOverlay { Input = "input" }; + + Assert.Null(model.Encoding); + Assert.False(model.RawData.ContainsKey("encoding")); + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new ImageOverlayImageOverlay { Input = "input" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ImageOverlayImageOverlay + { + Input = "input", + + // Null should be interpreted as omitted for these properties + Encoding = null, + Transformation = null, + }; + + Assert.Null(model.Encoding); + Assert.False(model.RawData.ContainsKey("encoding")); + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new ImageOverlayImageOverlay + { + Input = "input", + + // Null should be interpreted as omitted for these properties + Encoding = null, + Transformation = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ImageOverlayImageOverlay + { + Input = "input", + Encoding = Encoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + + ImageOverlayImageOverlay copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class EncodingTest : TestBase +{ + [Theory] + [InlineData(Encoding.Auto)] + [InlineData(Encoding.Plain)] + [InlineData(Encoding.Base64)] + public void Validation_Works(Encoding rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Encoding.Auto)] + [InlineData(Encoding.Plain)] + [InlineData(Encoding.Base64)] + public void SerializationRoundtrip_Works(Encoding rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/OverlayPositionTest.cs b/src/Imagekit.Tests/Models/OverlayPositionTest.cs new file mode 100644 index 00000000..a262e996 --- /dev/null +++ b/src/Imagekit.Tests/Models/OverlayPositionTest.cs @@ -0,0 +1,504 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class OverlayPositionTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new OverlayPosition + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }; + + ApiEnum expectedAnchorPoint = AnchorPoint.Top; + ApiEnum expectedFocus = Focus.Center; + X expectedX = 0; + XCenter expectedXCenter = 0; + Y expectedY = 0; + YCenter expectedYCenter = 0; + + Assert.Equal(expectedAnchorPoint, model.AnchorPoint); + Assert.Equal(expectedFocus, model.Focus); + Assert.Equal(expectedX, model.X); + Assert.Equal(expectedXCenter, model.XCenter); + Assert.Equal(expectedY, model.Y); + Assert.Equal(expectedYCenter, model.YCenter); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new OverlayPosition + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new OverlayPosition + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum expectedAnchorPoint = AnchorPoint.Top; + ApiEnum expectedFocus = Focus.Center; + X expectedX = 0; + XCenter expectedXCenter = 0; + Y expectedY = 0; + YCenter expectedYCenter = 0; + + Assert.Equal(expectedAnchorPoint, deserialized.AnchorPoint); + Assert.Equal(expectedFocus, deserialized.Focus); + Assert.Equal(expectedX, deserialized.X); + Assert.Equal(expectedXCenter, deserialized.XCenter); + Assert.Equal(expectedY, deserialized.Y); + Assert.Equal(expectedYCenter, deserialized.YCenter); + } + + [Fact] + public void Validation_Works() + { + var model = new OverlayPosition + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new OverlayPosition { }; + + Assert.Null(model.AnchorPoint); + Assert.False(model.RawData.ContainsKey("anchorPoint")); + Assert.Null(model.Focus); + Assert.False(model.RawData.ContainsKey("focus")); + Assert.Null(model.X); + Assert.False(model.RawData.ContainsKey("x")); + Assert.Null(model.XCenter); + Assert.False(model.RawData.ContainsKey("xCenter")); + Assert.Null(model.Y); + Assert.False(model.RawData.ContainsKey("y")); + Assert.Null(model.YCenter); + Assert.False(model.RawData.ContainsKey("yCenter")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new OverlayPosition { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new OverlayPosition + { + // Null should be interpreted as omitted for these properties + AnchorPoint = null, + Focus = null, + X = null, + XCenter = null, + Y = null, + YCenter = null, + }; + + Assert.Null(model.AnchorPoint); + Assert.False(model.RawData.ContainsKey("anchorPoint")); + Assert.Null(model.Focus); + Assert.False(model.RawData.ContainsKey("focus")); + Assert.Null(model.X); + Assert.False(model.RawData.ContainsKey("x")); + Assert.Null(model.XCenter); + Assert.False(model.RawData.ContainsKey("xCenter")); + Assert.Null(model.Y); + Assert.False(model.RawData.ContainsKey("y")); + Assert.Null(model.YCenter); + Assert.False(model.RawData.ContainsKey("yCenter")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new OverlayPosition + { + // Null should be interpreted as omitted for these properties + AnchorPoint = null, + Focus = null, + X = null, + XCenter = null, + Y = null, + YCenter = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new OverlayPosition + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }; + + OverlayPosition copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class AnchorPointTest : TestBase +{ + [Theory] + [InlineData(AnchorPoint.Top)] + [InlineData(AnchorPoint.Left)] + [InlineData(AnchorPoint.Right)] + [InlineData(AnchorPoint.Bottom)] + [InlineData(AnchorPoint.TopLeft)] + [InlineData(AnchorPoint.TopRight)] + [InlineData(AnchorPoint.BottomLeft)] + [InlineData(AnchorPoint.BottomRight)] + [InlineData(AnchorPoint.Center)] + public void Validation_Works(AnchorPoint rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(AnchorPoint.Top)] + [InlineData(AnchorPoint.Left)] + [InlineData(AnchorPoint.Right)] + [InlineData(AnchorPoint.Bottom)] + [InlineData(AnchorPoint.TopLeft)] + [InlineData(AnchorPoint.TopRight)] + [InlineData(AnchorPoint.BottomLeft)] + [InlineData(AnchorPoint.BottomRight)] + [InlineData(AnchorPoint.Center)] + public void SerializationRoundtrip_Works(AnchorPoint rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class FocusTest : TestBase +{ + [Theory] + [InlineData(Focus.Center)] + [InlineData(Focus.Top)] + [InlineData(Focus.Left)] + [InlineData(Focus.Bottom)] + [InlineData(Focus.Right)] + [InlineData(Focus.TopLeft)] + [InlineData(Focus.TopRight)] + [InlineData(Focus.BottomLeft)] + [InlineData(Focus.BottomRight)] + public void Validation_Works(Focus rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Focus.Center)] + [InlineData(Focus.Top)] + [InlineData(Focus.Left)] + [InlineData(Focus.Bottom)] + [InlineData(Focus.Right)] + [InlineData(Focus.TopLeft)] + [InlineData(Focus.TopRight)] + [InlineData(Focus.BottomLeft)] + [InlineData(Focus.BottomRight)] + public void SerializationRoundtrip_Works(Focus rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class XTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + X value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + X value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + X value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + X value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class XCenterTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + XCenter value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + XCenter value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + XCenter value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + XCenter value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class YTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + Y value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + Y value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + Y value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + Y value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class YCenterTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + YCenter value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + YCenter value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + YCenter value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + YCenter value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/OverlayTest.cs b/src/Imagekit.Tests/Models/OverlayTest.cs new file mode 100644 index 00000000..3153fcff --- /dev/null +++ b/src/Imagekit.Tests/Models/OverlayTest.cs @@ -0,0 +1,794 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class OverlayTest : TestBase +{ + [Fact] + public void TextValidationWorks() + { + Overlay value = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }; + value.Validate(); + } + + [Fact] + public void ImageValidationWorks() + { + Overlay value = new ImageOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = Encoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + value.Validate(); + } + + [Fact] + public void VideoValidationWorks() + { + Overlay value = new VideoOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = VideoOverlayVideoOverlayEncoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + value.Validate(); + } + + [Fact] + public void SubtitleValidationWorks() + { + Overlay value = new SubtitleOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = SubtitleOverlaySubtitleOverlayEncoding.Auto, + Transformation = + [ + new() + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }, + ], + }; + value.Validate(); + } + + [Fact] + public void SolidColorValidationWorks() + { + Overlay value = new SolidColorOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Color = "color", + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }, + ], + }; + value.Validate(); + } + + [Fact] + public void TextSerializationRoundtripWorks() + { + Overlay value = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void ImageSerializationRoundtripWorks() + { + Overlay value = new ImageOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = Encoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void VideoSerializationRoundtripWorks() + { + Overlay value = new VideoOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = VideoOverlayVideoOverlayEncoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void SubtitleSerializationRoundtripWorks() + { + Overlay value = new SubtitleOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = SubtitleOverlaySubtitleOverlayEncoding.Auto, + Transformation = + [ + new() + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }, + ], + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void SolidColorSerializationRoundtripWorks() + { + Overlay value = new SolidColorOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Color = "color", + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }, + ], + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/OverlayTimingTest.cs b/src/Imagekit.Tests/Models/OverlayTimingTest.cs new file mode 100644 index 00000000..30e0b1df --- /dev/null +++ b/src/Imagekit.Tests/Models/OverlayTimingTest.cs @@ -0,0 +1,271 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class OverlayTimingTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new OverlayTiming + { + Duration = 0, + End = 0, + Start = 0, + }; + + Duration expectedDuration = 0; + End expectedEnd = 0; + Start expectedStart = 0; + + Assert.Equal(expectedDuration, model.Duration); + Assert.Equal(expectedEnd, model.End); + Assert.Equal(expectedStart, model.Start); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new OverlayTiming + { + Duration = 0, + End = 0, + Start = 0, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new OverlayTiming + { + Duration = 0, + End = 0, + Start = 0, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + Duration expectedDuration = 0; + End expectedEnd = 0; + Start expectedStart = 0; + + Assert.Equal(expectedDuration, deserialized.Duration); + Assert.Equal(expectedEnd, deserialized.End); + Assert.Equal(expectedStart, deserialized.Start); + } + + [Fact] + public void Validation_Works() + { + var model = new OverlayTiming + { + Duration = 0, + End = 0, + Start = 0, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new OverlayTiming { }; + + Assert.Null(model.Duration); + Assert.False(model.RawData.ContainsKey("duration")); + Assert.Null(model.End); + Assert.False(model.RawData.ContainsKey("end")); + Assert.Null(model.Start); + Assert.False(model.RawData.ContainsKey("start")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new OverlayTiming { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new OverlayTiming + { + // Null should be interpreted as omitted for these properties + Duration = null, + End = null, + Start = null, + }; + + Assert.Null(model.Duration); + Assert.False(model.RawData.ContainsKey("duration")); + Assert.Null(model.End); + Assert.False(model.RawData.ContainsKey("end")); + Assert.Null(model.Start); + Assert.False(model.RawData.ContainsKey("start")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new OverlayTiming + { + // Null should be interpreted as omitted for these properties + Duration = null, + End = null, + Start = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new OverlayTiming + { + Duration = 0, + End = 0, + Start = 0, + }; + + OverlayTiming copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class DurationTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + Duration value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + Duration value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + Duration value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + Duration value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class EndTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + End value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + End value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + End value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + End value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class StartTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + Start value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + Start value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + Start value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + Start value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/ResponsiveImageAttributesTest.cs b/src/Imagekit.Tests/Models/ResponsiveImageAttributesTest.cs new file mode 100644 index 00000000..ff7a4c51 --- /dev/null +++ b/src/Imagekit.Tests/Models/ResponsiveImageAttributesTest.cs @@ -0,0 +1,180 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class ResponsiveImageAttributesTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ResponsiveImageAttributes + { + Src = "https://ik.imagekit.io/demo/image.jpg?tr=w-3840", + Sizes = "100vw", + SrcSet = + "https://ik.imagekit.io/demo/image.jpg?tr=w-640 640w, https://ik.imagekit.io/demo/image.jpg?tr=w-1080 1080w, https://ik.imagekit.io/demo/image.jpg?tr=w-1920 1920w", + Width = 400, + }; + + string expectedSrc = "https://ik.imagekit.io/demo/image.jpg?tr=w-3840"; + string expectedSizes = "100vw"; + string expectedSrcSet = + "https://ik.imagekit.io/demo/image.jpg?tr=w-640 640w, https://ik.imagekit.io/demo/image.jpg?tr=w-1080 1080w, https://ik.imagekit.io/demo/image.jpg?tr=w-1920 1920w"; + double expectedWidth = 400; + + Assert.Equal(expectedSrc, model.Src); + Assert.Equal(expectedSizes, model.Sizes); + Assert.Equal(expectedSrcSet, model.SrcSet); + Assert.Equal(expectedWidth, model.Width); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ResponsiveImageAttributes + { + Src = "https://ik.imagekit.io/demo/image.jpg?tr=w-3840", + Sizes = "100vw", + SrcSet = + "https://ik.imagekit.io/demo/image.jpg?tr=w-640 640w, https://ik.imagekit.io/demo/image.jpg?tr=w-1080 1080w, https://ik.imagekit.io/demo/image.jpg?tr=w-1920 1920w", + Width = 400, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ResponsiveImageAttributes + { + Src = "https://ik.imagekit.io/demo/image.jpg?tr=w-3840", + Sizes = "100vw", + SrcSet = + "https://ik.imagekit.io/demo/image.jpg?tr=w-640 640w, https://ik.imagekit.io/demo/image.jpg?tr=w-1080 1080w, https://ik.imagekit.io/demo/image.jpg?tr=w-1920 1920w", + Width = 400, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedSrc = "https://ik.imagekit.io/demo/image.jpg?tr=w-3840"; + string expectedSizes = "100vw"; + string expectedSrcSet = + "https://ik.imagekit.io/demo/image.jpg?tr=w-640 640w, https://ik.imagekit.io/demo/image.jpg?tr=w-1080 1080w, https://ik.imagekit.io/demo/image.jpg?tr=w-1920 1920w"; + double expectedWidth = 400; + + Assert.Equal(expectedSrc, deserialized.Src); + Assert.Equal(expectedSizes, deserialized.Sizes); + Assert.Equal(expectedSrcSet, deserialized.SrcSet); + Assert.Equal(expectedWidth, deserialized.Width); + } + + [Fact] + public void Validation_Works() + { + var model = new ResponsiveImageAttributes + { + Src = "https://ik.imagekit.io/demo/image.jpg?tr=w-3840", + Sizes = "100vw", + SrcSet = + "https://ik.imagekit.io/demo/image.jpg?tr=w-640 640w, https://ik.imagekit.io/demo/image.jpg?tr=w-1080 1080w, https://ik.imagekit.io/demo/image.jpg?tr=w-1920 1920w", + Width = 400, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ResponsiveImageAttributes + { + Src = "https://ik.imagekit.io/demo/image.jpg?tr=w-3840", + }; + + Assert.Null(model.Sizes); + Assert.False(model.RawData.ContainsKey("sizes")); + Assert.Null(model.SrcSet); + Assert.False(model.RawData.ContainsKey("srcSet")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new ResponsiveImageAttributes + { + Src = "https://ik.imagekit.io/demo/image.jpg?tr=w-3840", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ResponsiveImageAttributes + { + Src = "https://ik.imagekit.io/demo/image.jpg?tr=w-3840", + + // Null should be interpreted as omitted for these properties + Sizes = null, + SrcSet = null, + Width = null, + }; + + Assert.Null(model.Sizes); + Assert.False(model.RawData.ContainsKey("sizes")); + Assert.Null(model.SrcSet); + Assert.False(model.RawData.ContainsKey("srcSet")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new ResponsiveImageAttributes + { + Src = "https://ik.imagekit.io/demo/image.jpg?tr=w-3840", + + // Null should be interpreted as omitted for these properties + Sizes = null, + SrcSet = null, + Width = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ResponsiveImageAttributes + { + Src = "https://ik.imagekit.io/demo/image.jpg?tr=w-3840", + Sizes = "100vw", + SrcSet = + "https://ik.imagekit.io/demo/image.jpg?tr=w-640 640w, https://ik.imagekit.io/demo/image.jpg?tr=w-1080 1080w, https://ik.imagekit.io/demo/image.jpg?tr=w-1920 1920w", + Width = 400, + }; + + ResponsiveImageAttributes copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/SavedExtensions/SavedExtensionCreateParamsTest.cs b/src/Imagekit.Tests/Models/SavedExtensions/SavedExtensionCreateParamsTest.cs new file mode 100644 index 00000000..6592f141 --- /dev/null +++ b/src/Imagekit.Tests/Models/SavedExtensions/SavedExtensionCreateParamsTest.cs @@ -0,0 +1,96 @@ +using System; +using Imagekit.Models; +using Imagekit.Models.SavedExtensions; + +namespace Imagekit.Tests.Models.SavedExtensions; + +public class SavedExtensionCreateParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new SavedExtensionCreateParams + { + Config = new RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + Description = "Analyzes vehicle images for type, condition, and quality assessment", + Name = "Car Quality Analysis", + }; + + ExtensionConfig expectedConfig = new RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }; + string expectedDescription = + "Analyzes vehicle images for type, condition, and quality assessment"; + string expectedName = "Car Quality Analysis"; + + Assert.Equal(expectedConfig, parameters.Config); + Assert.Equal(expectedDescription, parameters.Description); + Assert.Equal(expectedName, parameters.Name); + } + + [Fact] + public void Url_Works() + { + SavedExtensionCreateParams parameters = new() + { + Config = new RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + Description = "Analyzes vehicle images for type, condition, and quality assessment", + Name = "Car Quality Analysis", + }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/saved-extensions"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new SavedExtensionCreateParams + { + Config = new RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + Description = "Analyzes vehicle images for type, condition, and quality assessment", + Name = "Car Quality Analysis", + }; + + SavedExtensionCreateParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/SavedExtensions/SavedExtensionDeleteParamsTest.cs b/src/Imagekit.Tests/Models/SavedExtensions/SavedExtensionDeleteParamsTest.cs new file mode 100644 index 00000000..348ea0de --- /dev/null +++ b/src/Imagekit.Tests/Models/SavedExtensions/SavedExtensionDeleteParamsTest.cs @@ -0,0 +1,39 @@ +using System; +using Imagekit.Models.SavedExtensions; + +namespace Imagekit.Tests.Models.SavedExtensions; + +public class SavedExtensionDeleteParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new SavedExtensionDeleteParams { ID = "id" }; + + string expectedID = "id"; + + Assert.Equal(expectedID, parameters.ID); + } + + [Fact] + public void Url_Works() + { + SavedExtensionDeleteParams parameters = new() { ID = "id" }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/saved-extensions/id"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new SavedExtensionDeleteParams { ID = "id" }; + + SavedExtensionDeleteParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/SavedExtensions/SavedExtensionGetParamsTest.cs b/src/Imagekit.Tests/Models/SavedExtensions/SavedExtensionGetParamsTest.cs new file mode 100644 index 00000000..28ad9a62 --- /dev/null +++ b/src/Imagekit.Tests/Models/SavedExtensions/SavedExtensionGetParamsTest.cs @@ -0,0 +1,39 @@ +using System; +using Imagekit.Models.SavedExtensions; + +namespace Imagekit.Tests.Models.SavedExtensions; + +public class SavedExtensionGetParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new SavedExtensionGetParams { ID = "id" }; + + string expectedID = "id"; + + Assert.Equal(expectedID, parameters.ID); + } + + [Fact] + public void Url_Works() + { + SavedExtensionGetParams parameters = new() { ID = "id" }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/saved-extensions/id"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new SavedExtensionGetParams { ID = "id" }; + + SavedExtensionGetParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/SavedExtensions/SavedExtensionListParamsTest.cs b/src/Imagekit.Tests/Models/SavedExtensions/SavedExtensionListParamsTest.cs new file mode 100644 index 00000000..5fce38dc --- /dev/null +++ b/src/Imagekit.Tests/Models/SavedExtensions/SavedExtensionListParamsTest.cs @@ -0,0 +1 @@ +namespace Imagekit.Tests.Models.SavedExtensions; diff --git a/src/Imagekit.Tests/Models/SavedExtensions/SavedExtensionUpdateParamsTest.cs b/src/Imagekit.Tests/Models/SavedExtensions/SavedExtensionUpdateParamsTest.cs new file mode 100644 index 00000000..65da1173 --- /dev/null +++ b/src/Imagekit.Tests/Models/SavedExtensions/SavedExtensionUpdateParamsTest.cs @@ -0,0 +1,119 @@ +using System; +using Imagekit.Models; +using Imagekit.Models.SavedExtensions; + +namespace Imagekit.Tests.Models.SavedExtensions; + +public class SavedExtensionUpdateParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new SavedExtensionUpdateParams + { + ID = "id", + Config = new RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + Description = "x", + Name = "x", + }; + + string expectedID = "id"; + ExtensionConfig expectedConfig = new RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }; + string expectedDescription = "x"; + string expectedName = "x"; + + Assert.Equal(expectedID, parameters.ID); + Assert.Equal(expectedConfig, parameters.Config); + Assert.Equal(expectedDescription, parameters.Description); + Assert.Equal(expectedName, parameters.Name); + } + + [Fact] + public void OptionalNonNullableParamsUnsetAreNotSet_Works() + { + var parameters = new SavedExtensionUpdateParams { ID = "id" }; + + Assert.Null(parameters.Config); + Assert.False(parameters.RawBodyData.ContainsKey("config")); + Assert.Null(parameters.Description); + Assert.False(parameters.RawBodyData.ContainsKey("description")); + Assert.Null(parameters.Name); + Assert.False(parameters.RawBodyData.ContainsKey("name")); + } + + [Fact] + public void OptionalNonNullableParamsSetToNullAreNotSet_Works() + { + var parameters = new SavedExtensionUpdateParams + { + ID = "id", + + // Null should be interpreted as omitted for these properties + Config = null, + Description = null, + Name = null, + }; + + Assert.Null(parameters.Config); + Assert.False(parameters.RawBodyData.ContainsKey("config")); + Assert.Null(parameters.Description); + Assert.False(parameters.RawBodyData.ContainsKey("description")); + Assert.Null(parameters.Name); + Assert.False(parameters.RawBodyData.ContainsKey("name")); + } + + [Fact] + public void Url_Works() + { + SavedExtensionUpdateParams parameters = new() { ID = "id" }; + + var url = parameters.Url(new() { PrivateKey = "My Private Key", Password = "My Password" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.imagekit.io/v1/saved-extensions/id"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new SavedExtensionUpdateParams + { + ID = "id", + Config = new RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + Description = "x", + Name = "x", + }; + + SavedExtensionUpdateParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Imagekit.Tests/Models/SelectedFieldsSchemaItemTest.cs b/src/Imagekit.Tests/Models/SelectedFieldsSchemaItemTest.cs new file mode 100644 index 00000000..41ffb13c --- /dev/null +++ b/src/Imagekit.Tests/Models/SelectedFieldsSchemaItemTest.cs @@ -0,0 +1,686 @@ +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class SelectedFieldsSchemaItemTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SelectedFieldsSchemaItem + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + }; + + ApiEnum expectedType = Type.Text; + DefaultValue expectedDefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ); + bool expectedIsValueRequired = true; + double expectedMaxLength = 0; + MaxValue expectedMaxValue = "string"; + double expectedMinLength = 0; + MinValue expectedMinValue = "string"; + bool expectedReadOnly = true; + List expectedSelectOptions = ["small", "medium", "large", 30, 40, true]; + bool expectedSelectOptionsTruncated = true; + + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedDefaultValue, model.DefaultValue); + Assert.Equal(expectedIsValueRequired, model.IsValueRequired); + Assert.Equal(expectedMaxLength, model.MaxLength); + Assert.Equal(expectedMaxValue, model.MaxValue); + Assert.Equal(expectedMinLength, model.MinLength); + Assert.Equal(expectedMinValue, model.MinValue); + Assert.Equal(expectedReadOnly, model.ReadOnly); + Assert.NotNull(model.SelectOptions); + Assert.Equal(expectedSelectOptions.Count, model.SelectOptions.Count); + for (int i = 0; i < expectedSelectOptions.Count; i++) + { + Assert.Equal(expectedSelectOptions[i], model.SelectOptions[i]); + } + Assert.Equal(expectedSelectOptionsTruncated, model.SelectOptionsTruncated); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SelectedFieldsSchemaItem + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SelectedFieldsSchemaItem + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum expectedType = Type.Text; + DefaultValue expectedDefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ); + bool expectedIsValueRequired = true; + double expectedMaxLength = 0; + MaxValue expectedMaxValue = "string"; + double expectedMinLength = 0; + MinValue expectedMinValue = "string"; + bool expectedReadOnly = true; + List expectedSelectOptions = ["small", "medium", "large", 30, 40, true]; + bool expectedSelectOptionsTruncated = true; + + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedDefaultValue, deserialized.DefaultValue); + Assert.Equal(expectedIsValueRequired, deserialized.IsValueRequired); + Assert.Equal(expectedMaxLength, deserialized.MaxLength); + Assert.Equal(expectedMaxValue, deserialized.MaxValue); + Assert.Equal(expectedMinLength, deserialized.MinLength); + Assert.Equal(expectedMinValue, deserialized.MinValue); + Assert.Equal(expectedReadOnly, deserialized.ReadOnly); + Assert.NotNull(deserialized.SelectOptions); + Assert.Equal(expectedSelectOptions.Count, deserialized.SelectOptions.Count); + for (int i = 0; i < expectedSelectOptions.Count; i++) + { + Assert.Equal(expectedSelectOptions[i], deserialized.SelectOptions[i]); + } + Assert.Equal(expectedSelectOptionsTruncated, deserialized.SelectOptionsTruncated); + } + + [Fact] + public void Validation_Works() + { + var model = new SelectedFieldsSchemaItem + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new SelectedFieldsSchemaItem { Type = Type.Text }; + + Assert.Null(model.DefaultValue); + Assert.False(model.RawData.ContainsKey("defaultValue")); + Assert.Null(model.IsValueRequired); + Assert.False(model.RawData.ContainsKey("isValueRequired")); + Assert.Null(model.MaxLength); + Assert.False(model.RawData.ContainsKey("maxLength")); + Assert.Null(model.MaxValue); + Assert.False(model.RawData.ContainsKey("maxValue")); + Assert.Null(model.MinLength); + Assert.False(model.RawData.ContainsKey("minLength")); + Assert.Null(model.MinValue); + Assert.False(model.RawData.ContainsKey("minValue")); + Assert.Null(model.ReadOnly); + Assert.False(model.RawData.ContainsKey("readOnly")); + Assert.Null(model.SelectOptions); + Assert.False(model.RawData.ContainsKey("selectOptions")); + Assert.Null(model.SelectOptionsTruncated); + Assert.False(model.RawData.ContainsKey("selectOptionsTruncated")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new SelectedFieldsSchemaItem { Type = Type.Text }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new SelectedFieldsSchemaItem + { + Type = Type.Text, + + // Null should be interpreted as omitted for these properties + DefaultValue = null, + IsValueRequired = null, + MaxLength = null, + MaxValue = null, + MinLength = null, + MinValue = null, + ReadOnly = null, + SelectOptions = null, + SelectOptionsTruncated = null, + }; + + Assert.Null(model.DefaultValue); + Assert.False(model.RawData.ContainsKey("defaultValue")); + Assert.Null(model.IsValueRequired); + Assert.False(model.RawData.ContainsKey("isValueRequired")); + Assert.Null(model.MaxLength); + Assert.False(model.RawData.ContainsKey("maxLength")); + Assert.Null(model.MaxValue); + Assert.False(model.RawData.ContainsKey("maxValue")); + Assert.Null(model.MinLength); + Assert.False(model.RawData.ContainsKey("minLength")); + Assert.Null(model.MinValue); + Assert.False(model.RawData.ContainsKey("minValue")); + Assert.Null(model.ReadOnly); + Assert.False(model.RawData.ContainsKey("readOnly")); + Assert.Null(model.SelectOptions); + Assert.False(model.RawData.ContainsKey("selectOptions")); + Assert.Null(model.SelectOptionsTruncated); + Assert.False(model.RawData.ContainsKey("selectOptionsTruncated")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new SelectedFieldsSchemaItem + { + Type = Type.Text, + + // Null should be interpreted as omitted for these properties + DefaultValue = null, + IsValueRequired = null, + MaxLength = null, + MaxValue = null, + MinLength = null, + MinValue = null, + ReadOnly = null, + SelectOptions = null, + SelectOptionsTruncated = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SelectedFieldsSchemaItem + { + Type = Type.Text, + DefaultValue = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + }; + + SelectedFieldsSchemaItem copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class TypeTest : TestBase +{ + [Theory] + [InlineData(Type.Text)] + [InlineData(Type.Textarea)] + [InlineData(Type.Number)] + [InlineData(Type.Date)] + [InlineData(Type.Boolean)] + [InlineData(Type.SingleSelect)] + [InlineData(Type.MultiSelect)] + public void Validation_Works(Type rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Type.Text)] + [InlineData(Type.Textarea)] + [InlineData(Type.Number)] + [InlineData(Type.Date)] + [InlineData(Type.Boolean)] + [InlineData(Type.SingleSelect)] + [InlineData(Type.MultiSelect)] + public void SerializationRoundtrip_Works(Type rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class DefaultValueTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + DefaultValue value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + DefaultValue value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + DefaultValue value = true; + value.Validate(); + } + + [Fact] + public void MixedValidationWorks() + { + DefaultValue value = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ); + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + DefaultValue value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + DefaultValue value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + DefaultValue value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void MixedSerializationRoundtripWorks() + { + DefaultValue value = new( + [ + new DefaultValueArrayItem(true), + new DefaultValueArrayItem(10), + new DefaultValueArrayItem("Hello"), + ] + ); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class DefaultValueArrayItemTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + DefaultValueArrayItem value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + DefaultValueArrayItem value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + DefaultValueArrayItem value = true; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + DefaultValueArrayItem value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + DefaultValueArrayItem value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + DefaultValueArrayItem value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class MaxValueTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + MaxValue value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + MaxValue value = 0; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + MaxValue value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + MaxValue value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class MinValueTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + MinValue value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + MinValue value = 0; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + MinValue value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + MinValue value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class SelectOptionTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + SelectOption value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + SelectOption value = 0; + value.Validate(); + } + + [Fact] + public void BoolValidationWorks() + { + SelectOption value = true; + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + SelectOption value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + SelectOption value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void BoolSerializationRoundtripWorks() + { + SelectOption value = true; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/SharedSavedExtensionTest.cs b/src/Imagekit.Tests/Models/SharedSavedExtensionTest.cs new file mode 100644 index 00000000..b513989e --- /dev/null +++ b/src/Imagekit.Tests/Models/SharedSavedExtensionTest.cs @@ -0,0 +1,265 @@ +using System; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class SharedSavedExtensionTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SharedSavedExtension + { + ID = "ext_abc123", + Config = new RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Description = "Analyzes vehicle images for type, condition, and quality assessment", + Name = "Car Quality Analysis", + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + }; + + string expectedID = "ext_abc123"; + ExtensionConfig expectedConfig = new RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + string expectedDescription = + "Analyzes vehicle images for type, condition, and quality assessment"; + string expectedName = "Car Quality Analysis"; + DateTimeOffset expectedUpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedConfig, model.Config); + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedDescription, model.Description); + Assert.Equal(expectedName, model.Name); + Assert.Equal(expectedUpdatedAt, model.UpdatedAt); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SharedSavedExtension + { + ID = "ext_abc123", + Config = new RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Description = "Analyzes vehicle images for type, condition, and quality assessment", + Name = "Car Quality Analysis", + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SharedSavedExtension + { + ID = "ext_abc123", + Config = new RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Description = "Analyzes vehicle images for type, condition, and quality assessment", + Name = "Car Quality Analysis", + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "ext_abc123"; + ExtensionConfig expectedConfig = new RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + string expectedDescription = + "Analyzes vehicle images for type, condition, and quality assessment"; + string expectedName = "Car Quality Analysis"; + DateTimeOffset expectedUpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedConfig, deserialized.Config); + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedDescription, deserialized.Description); + Assert.Equal(expectedName, deserialized.Name); + Assert.Equal(expectedUpdatedAt, deserialized.UpdatedAt); + } + + [Fact] + public void Validation_Works() + { + var model = new SharedSavedExtension + { + ID = "ext_abc123", + Config = new RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Description = "Analyzes vehicle images for type, condition, and quality assessment", + Name = "Car Quality Analysis", + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new SharedSavedExtension { }; + + Assert.Null(model.ID); + Assert.False(model.RawData.ContainsKey("id")); + Assert.Null(model.Config); + Assert.False(model.RawData.ContainsKey("config")); + Assert.Null(model.CreatedAt); + Assert.False(model.RawData.ContainsKey("createdAt")); + Assert.Null(model.Description); + Assert.False(model.RawData.ContainsKey("description")); + Assert.Null(model.Name); + Assert.False(model.RawData.ContainsKey("name")); + Assert.Null(model.UpdatedAt); + Assert.False(model.RawData.ContainsKey("updatedAt")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new SharedSavedExtension { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new SharedSavedExtension + { + // Null should be interpreted as omitted for these properties + ID = null, + Config = null, + CreatedAt = null, + Description = null, + Name = null, + UpdatedAt = null, + }; + + Assert.Null(model.ID); + Assert.False(model.RawData.ContainsKey("id")); + Assert.Null(model.Config); + Assert.False(model.RawData.ContainsKey("config")); + Assert.Null(model.CreatedAt); + Assert.False(model.RawData.ContainsKey("createdAt")); + Assert.Null(model.Description); + Assert.False(model.RawData.ContainsKey("description")); + Assert.Null(model.Name); + Assert.False(model.RawData.ContainsKey("name")); + Assert.Null(model.UpdatedAt); + Assert.False(model.RawData.ContainsKey("updatedAt")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new SharedSavedExtension + { + // Null should be interpreted as omitted for these properties + ID = null, + Config = null, + CreatedAt = null, + Description = null, + Name = null, + UpdatedAt = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SharedSavedExtension + { + ID = "ext_abc123", + Config = new RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Description = "Analyzes vehicle images for type, condition, and quality assessment", + Name = "Car Quality Analysis", + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + }; + + SharedSavedExtension copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/SolidColorOverlayTest.cs b/src/Imagekit.Tests/Models/SolidColorOverlayTest.cs new file mode 100644 index 00000000..8996acac --- /dev/null +++ b/src/Imagekit.Tests/Models/SolidColorOverlayTest.cs @@ -0,0 +1,588 @@ +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class SolidColorOverlayTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SolidColorOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Color = "color", + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }, + ], + }; + + ApiEnum expectedLayerMode = LayerMode.Multiply; + OverlayPosition expectedPosition = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }; + OverlayTiming expectedTiming = new() + { + Duration = 0, + End = 0, + Start = 0, + }; + string expectedColor = "color"; + JsonElement expectedType = JsonSerializer.SerializeToElement("solidColor"); + List expectedTransformation = + [ + new() + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }, + ]; + + Assert.Equal(expectedLayerMode, model.LayerMode); + Assert.Equal(expectedPosition, model.Position); + Assert.Equal(expectedTiming, model.Timing); + Assert.Equal(expectedColor, model.Color); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.NotNull(model.Transformation); + Assert.Equal(expectedTransformation.Count, model.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], model.Transformation[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SolidColorOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Color = "color", + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }, + ], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SolidColorOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Color = "color", + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }, + ], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum expectedLayerMode = LayerMode.Multiply; + OverlayPosition expectedPosition = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }; + OverlayTiming expectedTiming = new() + { + Duration = 0, + End = 0, + Start = 0, + }; + string expectedColor = "color"; + JsonElement expectedType = JsonSerializer.SerializeToElement("solidColor"); + List expectedTransformation = + [ + new() + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }, + ]; + + Assert.Equal(expectedLayerMode, deserialized.LayerMode); + Assert.Equal(expectedPosition, deserialized.Position); + Assert.Equal(expectedTiming, deserialized.Timing); + Assert.Equal(expectedColor, deserialized.Color); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.NotNull(deserialized.Transformation); + Assert.Equal(expectedTransformation.Count, deserialized.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], deserialized.Transformation[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new SolidColorOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Color = "color", + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }, + ], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new SolidColorOverlay { Color = "color" }; + + Assert.Null(model.LayerMode); + Assert.False(model.RawData.ContainsKey("layerMode")); + Assert.Null(model.Position); + Assert.False(model.RawData.ContainsKey("position")); + Assert.Null(model.Timing); + Assert.False(model.RawData.ContainsKey("timing")); + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new SolidColorOverlay { Color = "color" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new SolidColorOverlay + { + Color = "color", + + // Null should be interpreted as omitted for these properties + LayerMode = null, + Position = null, + Timing = null, + Transformation = null, + }; + + Assert.Null(model.LayerMode); + Assert.False(model.RawData.ContainsKey("layerMode")); + Assert.Null(model.Position); + Assert.False(model.RawData.ContainsKey("position")); + Assert.Null(model.Timing); + Assert.False(model.RawData.ContainsKey("timing")); + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new SolidColorOverlay + { + Color = "color", + + // Null should be interpreted as omitted for these properties + LayerMode = null, + Position = null, + Timing = null, + Transformation = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SolidColorOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Color = "color", + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }, + ], + }; + + SolidColorOverlay copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class SolidColorOverlaySolidColorOverlayTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SolidColorOverlaySolidColorOverlay + { + Color = "color", + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }, + ], + }; + + string expectedColor = "color"; + JsonElement expectedType = JsonSerializer.SerializeToElement("solidColor"); + List expectedTransformation = + [ + new() + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }, + ]; + + Assert.Equal(expectedColor, model.Color); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.NotNull(model.Transformation); + Assert.Equal(expectedTransformation.Count, model.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], model.Transformation[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SolidColorOverlaySolidColorOverlay + { + Color = "color", + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }, + ], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SolidColorOverlaySolidColorOverlay + { + Color = "color", + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }, + ], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedColor = "color"; + JsonElement expectedType = JsonSerializer.SerializeToElement("solidColor"); + List expectedTransformation = + [ + new() + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }, + ]; + + Assert.Equal(expectedColor, deserialized.Color); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.NotNull(deserialized.Transformation); + Assert.Equal(expectedTransformation.Count, deserialized.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], deserialized.Transformation[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new SolidColorOverlaySolidColorOverlay + { + Color = "color", + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }, + ], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new SolidColorOverlaySolidColorOverlay { Color = "color" }; + + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new SolidColorOverlaySolidColorOverlay { Color = "color" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new SolidColorOverlaySolidColorOverlay + { + Color = "color", + + // Null should be interpreted as omitted for these properties + Transformation = null, + }; + + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new SolidColorOverlaySolidColorOverlay + { + Color = "color", + + // Null should be interpreted as omitted for these properties + Transformation = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SolidColorOverlaySolidColorOverlay + { + Color = "color", + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }, + ], + }; + + SolidColorOverlaySolidColorOverlay copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/SolidColorOverlayTransformationTest.cs b/src/Imagekit.Tests/Models/SolidColorOverlayTransformationTest.cs new file mode 100644 index 00000000..0eb81d83 --- /dev/null +++ b/src/Imagekit.Tests/Models/SolidColorOverlayTransformationTest.cs @@ -0,0 +1,520 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class SolidColorOverlayTransformationTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SolidColorOverlayTransformation + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }; + + double expectedAlpha = 1; + string expectedBackground = "background"; + Gradient expectedGradient = new Default(); + Height expectedHeight = 0; + Radius expectedRadius = new Max(); + Width expectedWidth = 0; + + Assert.Equal(expectedAlpha, model.Alpha); + Assert.Equal(expectedBackground, model.Background); + Assert.Equal(expectedGradient, model.Gradient); + Assert.Equal(expectedHeight, model.Height); + Assert.Equal(expectedRadius, model.Radius); + Assert.Equal(expectedWidth, model.Width); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SolidColorOverlayTransformation + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SolidColorOverlayTransformation + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + double expectedAlpha = 1; + string expectedBackground = "background"; + Gradient expectedGradient = new Default(); + Height expectedHeight = 0; + Radius expectedRadius = new Max(); + Width expectedWidth = 0; + + Assert.Equal(expectedAlpha, deserialized.Alpha); + Assert.Equal(expectedBackground, deserialized.Background); + Assert.Equal(expectedGradient, deserialized.Gradient); + Assert.Equal(expectedHeight, deserialized.Height); + Assert.Equal(expectedRadius, deserialized.Radius); + Assert.Equal(expectedWidth, deserialized.Width); + } + + [Fact] + public void Validation_Works() + { + var model = new SolidColorOverlayTransformation + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new SolidColorOverlayTransformation { }; + + Assert.Null(model.Alpha); + Assert.False(model.RawData.ContainsKey("alpha")); + Assert.Null(model.Background); + Assert.False(model.RawData.ContainsKey("background")); + Assert.Null(model.Gradient); + Assert.False(model.RawData.ContainsKey("gradient")); + Assert.Null(model.Height); + Assert.False(model.RawData.ContainsKey("height")); + Assert.Null(model.Radius); + Assert.False(model.RawData.ContainsKey("radius")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new SolidColorOverlayTransformation { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new SolidColorOverlayTransformation + { + // Null should be interpreted as omitted for these properties + Alpha = null, + Background = null, + Gradient = null, + Height = null, + Radius = null, + Width = null, + }; + + Assert.Null(model.Alpha); + Assert.False(model.RawData.ContainsKey("alpha")); + Assert.Null(model.Background); + Assert.False(model.RawData.ContainsKey("background")); + Assert.Null(model.Gradient); + Assert.False(model.RawData.ContainsKey("gradient")); + Assert.Null(model.Height); + Assert.False(model.RawData.ContainsKey("height")); + Assert.Null(model.Radius); + Assert.False(model.RawData.ContainsKey("radius")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new SolidColorOverlayTransformation + { + // Null should be interpreted as omitted for these properties + Alpha = null, + Background = null, + Gradient = null, + Height = null, + Radius = null, + Width = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SolidColorOverlayTransformation + { + Alpha = 1, + Background = "background", + Gradient = new Default(), + Height = 0, + Radius = new Max(), + Width = 0, + }; + + SolidColorOverlayTransformation copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class GradientTest : TestBase +{ + [Fact] + public void DefaultValidationWorks() + { + Gradient value = new Default(); + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + Gradient value = "string"; + value.Validate(); + } + + [Fact] + public void DefaultSerializationRoundtripWorks() + { + Gradient value = new Default(); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + Gradient value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class DefaultTest : TestBase +{ + [Fact] + public void DefaultValidation_Works() + { + var constant = new Default(); + constant.Validate(); + } + + [Fact] + public void ValidConstantValidation_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement(true), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + constant.Validate(); + } + + [Fact] + public void InvalidConstantValidationThrows_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + Assert.Throws(() => constant.Validate()); + } + + [Fact] + public void DefaultRoundtrip_Works() + { + var constant = new Default(); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void ValidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement(true), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void InvalidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } +} + +public class HeightTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + Height value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + Height value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + Height value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + Height value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class RadiusTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + Radius value = 0; + value.Validate(); + } + + [Fact] + public void MaxValidationWorks() + { + Radius value = new Max(); + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + Radius value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + Radius value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void MaxSerializationRoundtripWorks() + { + Radius value = new Max(); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + Radius value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class MaxTest : TestBase +{ + [Fact] + public void DefaultValidation_Works() + { + var constant = new Max(); + constant.Validate(); + } + + [Fact] + public void ValidConstantValidation_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("max"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + constant.Validate(); + } + + [Fact] + public void InvalidConstantValidationThrows_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + Assert.Throws(() => constant.Validate()); + } + + [Fact] + public void DefaultRoundtrip_Works() + { + var constant = new Max(); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void ValidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("max"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void InvalidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(constant, deserialized); + } +} + +public class WidthTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + Width value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + Width value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + Width value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + Width value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/SrcOptionsTest.cs b/src/Imagekit.Tests/Models/SrcOptionsTest.cs new file mode 100644 index 00000000..330caf5a --- /dev/null +++ b/src/Imagekit.Tests/Models/SrcOptionsTest.cs @@ -0,0 +1,936 @@ +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class SrcOptionsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SrcOptions + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + ExpiresIn = 0, + QueryParameters = new Dictionary() { { "foo", "string" } }, + Signed = true, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + TransformationPosition = TransformationPosition.Path, + }; + + string expectedSrc = "/my-image.jpg"; + string expectedUrlEndpoint = "https://ik.imagekit.io/demo"; + double expectedExpiresIn = 0; + Dictionary expectedQueryParameters = new() { { "foo", "string" } }; + bool expectedSigned = true; + List expectedTransformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ]; + ApiEnum expectedTransformationPosition = + TransformationPosition.Path; + + Assert.Equal(expectedSrc, model.Src); + Assert.Equal(expectedUrlEndpoint, model.UrlEndpoint); + Assert.Equal(expectedExpiresIn, model.ExpiresIn); + Assert.NotNull(model.QueryParameters); + Assert.Equal(expectedQueryParameters.Count, model.QueryParameters.Count); + foreach (var item in expectedQueryParameters) + { + Assert.True(model.QueryParameters.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.QueryParameters[item.Key]); + } + Assert.Equal(expectedSigned, model.Signed); + Assert.NotNull(model.Transformation); + Assert.Equal(expectedTransformation.Count, model.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], model.Transformation[i]); + } + Assert.Equal(expectedTransformationPosition, model.TransformationPosition); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SrcOptions + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + ExpiresIn = 0, + QueryParameters = new Dictionary() { { "foo", "string" } }, + Signed = true, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + TransformationPosition = TransformationPosition.Path, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SrcOptions + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + ExpiresIn = 0, + QueryParameters = new Dictionary() { { "foo", "string" } }, + Signed = true, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + TransformationPosition = TransformationPosition.Path, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedSrc = "/my-image.jpg"; + string expectedUrlEndpoint = "https://ik.imagekit.io/demo"; + double expectedExpiresIn = 0; + Dictionary expectedQueryParameters = new() { { "foo", "string" } }; + bool expectedSigned = true; + List expectedTransformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ]; + ApiEnum expectedTransformationPosition = + TransformationPosition.Path; + + Assert.Equal(expectedSrc, deserialized.Src); + Assert.Equal(expectedUrlEndpoint, deserialized.UrlEndpoint); + Assert.Equal(expectedExpiresIn, deserialized.ExpiresIn); + Assert.NotNull(deserialized.QueryParameters); + Assert.Equal(expectedQueryParameters.Count, deserialized.QueryParameters.Count); + foreach (var item in expectedQueryParameters) + { + Assert.True(deserialized.QueryParameters.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.QueryParameters[item.Key]); + } + Assert.Equal(expectedSigned, deserialized.Signed); + Assert.NotNull(deserialized.Transformation); + Assert.Equal(expectedTransformation.Count, deserialized.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], deserialized.Transformation[i]); + } + Assert.Equal(expectedTransformationPosition, deserialized.TransformationPosition); + } + + [Fact] + public void Validation_Works() + { + var model = new SrcOptions + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + ExpiresIn = 0, + QueryParameters = new Dictionary() { { "foo", "string" } }, + Signed = true, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + TransformationPosition = TransformationPosition.Path, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new SrcOptions + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + }; + + Assert.Null(model.ExpiresIn); + Assert.False(model.RawData.ContainsKey("expiresIn")); + Assert.Null(model.QueryParameters); + Assert.False(model.RawData.ContainsKey("queryParameters")); + Assert.Null(model.Signed); + Assert.False(model.RawData.ContainsKey("signed")); + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + Assert.Null(model.TransformationPosition); + Assert.False(model.RawData.ContainsKey("transformationPosition")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new SrcOptions + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new SrcOptions + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + + // Null should be interpreted as omitted for these properties + ExpiresIn = null, + QueryParameters = null, + Signed = null, + Transformation = null, + TransformationPosition = null, + }; + + Assert.Null(model.ExpiresIn); + Assert.False(model.RawData.ContainsKey("expiresIn")); + Assert.Null(model.QueryParameters); + Assert.False(model.RawData.ContainsKey("queryParameters")); + Assert.Null(model.Signed); + Assert.False(model.RawData.ContainsKey("signed")); + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + Assert.Null(model.TransformationPosition); + Assert.False(model.RawData.ContainsKey("transformationPosition")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new SrcOptions + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + + // Null should be interpreted as omitted for these properties + ExpiresIn = null, + QueryParameters = null, + Signed = null, + Transformation = null, + TransformationPosition = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SrcOptions + { + Src = "/my-image.jpg", + UrlEndpoint = "https://ik.imagekit.io/demo", + ExpiresIn = 0, + QueryParameters = new Dictionary() { { "foo", "string" } }, + Signed = true, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + TransformationPosition = TransformationPosition.Path, + }; + + SrcOptions copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/StreamingResolutionTest.cs b/src/Imagekit.Tests/Models/StreamingResolutionTest.cs new file mode 100644 index 00000000..54ceb375 --- /dev/null +++ b/src/Imagekit.Tests/Models/StreamingResolutionTest.cs @@ -0,0 +1,74 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class StreamingResolutionTest : TestBase +{ + [Theory] + [InlineData(StreamingResolution.V240)] + [InlineData(StreamingResolution.V360)] + [InlineData(StreamingResolution.V480)] + [InlineData(StreamingResolution.V720)] + [InlineData(StreamingResolution.V1080)] + [InlineData(StreamingResolution.V1440)] + [InlineData(StreamingResolution.V2160)] + public void Validation_Works(StreamingResolution rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(StreamingResolution.V240)] + [InlineData(StreamingResolution.V360)] + [InlineData(StreamingResolution.V480)] + [InlineData(StreamingResolution.V720)] + [InlineData(StreamingResolution.V1080)] + [InlineData(StreamingResolution.V1440)] + [InlineData(StreamingResolution.V2160)] + public void SerializationRoundtrip_Works(StreamingResolution rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/SubtitleOverlayTest.cs b/src/Imagekit.Tests/Models/SubtitleOverlayTest.cs new file mode 100644 index 00000000..210029ac --- /dev/null +++ b/src/Imagekit.Tests/Models/SubtitleOverlayTest.cs @@ -0,0 +1,693 @@ +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class SubtitleOverlayTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SubtitleOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = SubtitleOverlaySubtitleOverlayEncoding.Auto, + Transformation = + [ + new() + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }, + ], + }; + + ApiEnum expectedLayerMode = LayerMode.Multiply; + OverlayPosition expectedPosition = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }; + OverlayTiming expectedTiming = new() + { + Duration = 0, + End = 0, + Start = 0, + }; + string expectedInput = "input"; + JsonElement expectedType = JsonSerializer.SerializeToElement("subtitle"); + ApiEnum expectedEncoding = + SubtitleOverlaySubtitleOverlayEncoding.Auto; + List expectedTransformation = + [ + new() + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }, + ]; + + Assert.Equal(expectedLayerMode, model.LayerMode); + Assert.Equal(expectedPosition, model.Position); + Assert.Equal(expectedTiming, model.Timing); + Assert.Equal(expectedInput, model.Input); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedEncoding, model.Encoding); + Assert.NotNull(model.Transformation); + Assert.Equal(expectedTransformation.Count, model.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], model.Transformation[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SubtitleOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = SubtitleOverlaySubtitleOverlayEncoding.Auto, + Transformation = + [ + new() + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }, + ], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SubtitleOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = SubtitleOverlaySubtitleOverlayEncoding.Auto, + Transformation = + [ + new() + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }, + ], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum expectedLayerMode = LayerMode.Multiply; + OverlayPosition expectedPosition = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }; + OverlayTiming expectedTiming = new() + { + Duration = 0, + End = 0, + Start = 0, + }; + string expectedInput = "input"; + JsonElement expectedType = JsonSerializer.SerializeToElement("subtitle"); + ApiEnum expectedEncoding = + SubtitleOverlaySubtitleOverlayEncoding.Auto; + List expectedTransformation = + [ + new() + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }, + ]; + + Assert.Equal(expectedLayerMode, deserialized.LayerMode); + Assert.Equal(expectedPosition, deserialized.Position); + Assert.Equal(expectedTiming, deserialized.Timing); + Assert.Equal(expectedInput, deserialized.Input); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedEncoding, deserialized.Encoding); + Assert.NotNull(deserialized.Transformation); + Assert.Equal(expectedTransformation.Count, deserialized.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], deserialized.Transformation[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new SubtitleOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = SubtitleOverlaySubtitleOverlayEncoding.Auto, + Transformation = + [ + new() + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }, + ], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new SubtitleOverlay { Input = "input" }; + + Assert.Null(model.LayerMode); + Assert.False(model.RawData.ContainsKey("layerMode")); + Assert.Null(model.Position); + Assert.False(model.RawData.ContainsKey("position")); + Assert.Null(model.Timing); + Assert.False(model.RawData.ContainsKey("timing")); + Assert.Null(model.Encoding); + Assert.False(model.RawData.ContainsKey("encoding")); + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new SubtitleOverlay { Input = "input" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new SubtitleOverlay + { + Input = "input", + + // Null should be interpreted as omitted for these properties + LayerMode = null, + Position = null, + Timing = null, + Encoding = null, + Transformation = null, + }; + + Assert.Null(model.LayerMode); + Assert.False(model.RawData.ContainsKey("layerMode")); + Assert.Null(model.Position); + Assert.False(model.RawData.ContainsKey("position")); + Assert.Null(model.Timing); + Assert.False(model.RawData.ContainsKey("timing")); + Assert.Null(model.Encoding); + Assert.False(model.RawData.ContainsKey("encoding")); + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new SubtitleOverlay + { + Input = "input", + + // Null should be interpreted as omitted for these properties + LayerMode = null, + Position = null, + Timing = null, + Encoding = null, + Transformation = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SubtitleOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = SubtitleOverlaySubtitleOverlayEncoding.Auto, + Transformation = + [ + new() + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }, + ], + }; + + SubtitleOverlay copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class SubtitleOverlaySubtitleOverlayTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SubtitleOverlaySubtitleOverlay + { + Input = "input", + Encoding = SubtitleOverlaySubtitleOverlayEncoding.Auto, + Transformation = + [ + new() + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }, + ], + }; + + string expectedInput = "input"; + JsonElement expectedType = JsonSerializer.SerializeToElement("subtitle"); + ApiEnum expectedEncoding = + SubtitleOverlaySubtitleOverlayEncoding.Auto; + List expectedTransformation = + [ + new() + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }, + ]; + + Assert.Equal(expectedInput, model.Input); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedEncoding, model.Encoding); + Assert.NotNull(model.Transformation); + Assert.Equal(expectedTransformation.Count, model.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], model.Transformation[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SubtitleOverlaySubtitleOverlay + { + Input = "input", + Encoding = SubtitleOverlaySubtitleOverlayEncoding.Auto, + Transformation = + [ + new() + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }, + ], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SubtitleOverlaySubtitleOverlay + { + Input = "input", + Encoding = SubtitleOverlaySubtitleOverlayEncoding.Auto, + Transformation = + [ + new() + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }, + ], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedInput = "input"; + JsonElement expectedType = JsonSerializer.SerializeToElement("subtitle"); + ApiEnum expectedEncoding = + SubtitleOverlaySubtitleOverlayEncoding.Auto; + List expectedTransformation = + [ + new() + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }, + ]; + + Assert.Equal(expectedInput, deserialized.Input); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedEncoding, deserialized.Encoding); + Assert.NotNull(deserialized.Transformation); + Assert.Equal(expectedTransformation.Count, deserialized.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], deserialized.Transformation[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new SubtitleOverlaySubtitleOverlay + { + Input = "input", + Encoding = SubtitleOverlaySubtitleOverlayEncoding.Auto, + Transformation = + [ + new() + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }, + ], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new SubtitleOverlaySubtitleOverlay { Input = "input" }; + + Assert.Null(model.Encoding); + Assert.False(model.RawData.ContainsKey("encoding")); + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new SubtitleOverlaySubtitleOverlay { Input = "input" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new SubtitleOverlaySubtitleOverlay + { + Input = "input", + + // Null should be interpreted as omitted for these properties + Encoding = null, + Transformation = null, + }; + + Assert.Null(model.Encoding); + Assert.False(model.RawData.ContainsKey("encoding")); + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new SubtitleOverlaySubtitleOverlay + { + Input = "input", + + // Null should be interpreted as omitted for these properties + Encoding = null, + Transformation = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SubtitleOverlaySubtitleOverlay + { + Input = "input", + Encoding = SubtitleOverlaySubtitleOverlayEncoding.Auto, + Transformation = + [ + new() + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }, + ], + }; + + SubtitleOverlaySubtitleOverlay copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class SubtitleOverlaySubtitleOverlayEncodingTest : TestBase +{ + [Theory] + [InlineData(SubtitleOverlaySubtitleOverlayEncoding.Auto)] + [InlineData(SubtitleOverlaySubtitleOverlayEncoding.Plain)] + [InlineData(SubtitleOverlaySubtitleOverlayEncoding.Base64)] + public void Validation_Works(SubtitleOverlaySubtitleOverlayEncoding rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(SubtitleOverlaySubtitleOverlayEncoding.Auto)] + [InlineData(SubtitleOverlaySubtitleOverlayEncoding.Plain)] + [InlineData(SubtitleOverlaySubtitleOverlayEncoding.Base64)] + public void SerializationRoundtrip_Works(SubtitleOverlaySubtitleOverlayEncoding rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/SubtitleOverlayTransformationTest.cs b/src/Imagekit.Tests/Models/SubtitleOverlayTransformationTest.cs new file mode 100644 index 00000000..5a962a02 --- /dev/null +++ b/src/Imagekit.Tests/Models/SubtitleOverlayTransformationTest.cs @@ -0,0 +1,275 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class SubtitleOverlayTransformationTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SubtitleOverlayTransformation + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }; + + string expectedBackground = "background"; + string expectedColor = "color"; + string expectedFontFamily = "fontFamily"; + string expectedFontOutline = "fontOutline"; + string expectedFontShadow = "fontShadow"; + double expectedFontSize = 0; + ApiEnum expectedTypography = Typography.B; + + Assert.Equal(expectedBackground, model.Background); + Assert.Equal(expectedColor, model.Color); + Assert.Equal(expectedFontFamily, model.FontFamily); + Assert.Equal(expectedFontOutline, model.FontOutline); + Assert.Equal(expectedFontShadow, model.FontShadow); + Assert.Equal(expectedFontSize, model.FontSize); + Assert.Equal(expectedTypography, model.Typography); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SubtitleOverlayTransformation + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SubtitleOverlayTransformation + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedBackground = "background"; + string expectedColor = "color"; + string expectedFontFamily = "fontFamily"; + string expectedFontOutline = "fontOutline"; + string expectedFontShadow = "fontShadow"; + double expectedFontSize = 0; + ApiEnum expectedTypography = Typography.B; + + Assert.Equal(expectedBackground, deserialized.Background); + Assert.Equal(expectedColor, deserialized.Color); + Assert.Equal(expectedFontFamily, deserialized.FontFamily); + Assert.Equal(expectedFontOutline, deserialized.FontOutline); + Assert.Equal(expectedFontShadow, deserialized.FontShadow); + Assert.Equal(expectedFontSize, deserialized.FontSize); + Assert.Equal(expectedTypography, deserialized.Typography); + } + + [Fact] + public void Validation_Works() + { + var model = new SubtitleOverlayTransformation + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new SubtitleOverlayTransformation { }; + + Assert.Null(model.Background); + Assert.False(model.RawData.ContainsKey("background")); + Assert.Null(model.Color); + Assert.False(model.RawData.ContainsKey("color")); + Assert.Null(model.FontFamily); + Assert.False(model.RawData.ContainsKey("fontFamily")); + Assert.Null(model.FontOutline); + Assert.False(model.RawData.ContainsKey("fontOutline")); + Assert.Null(model.FontShadow); + Assert.False(model.RawData.ContainsKey("fontShadow")); + Assert.Null(model.FontSize); + Assert.False(model.RawData.ContainsKey("fontSize")); + Assert.Null(model.Typography); + Assert.False(model.RawData.ContainsKey("typography")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new SubtitleOverlayTransformation { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new SubtitleOverlayTransformation + { + // Null should be interpreted as omitted for these properties + Background = null, + Color = null, + FontFamily = null, + FontOutline = null, + FontShadow = null, + FontSize = null, + Typography = null, + }; + + Assert.Null(model.Background); + Assert.False(model.RawData.ContainsKey("background")); + Assert.Null(model.Color); + Assert.False(model.RawData.ContainsKey("color")); + Assert.Null(model.FontFamily); + Assert.False(model.RawData.ContainsKey("fontFamily")); + Assert.Null(model.FontOutline); + Assert.False(model.RawData.ContainsKey("fontOutline")); + Assert.Null(model.FontShadow); + Assert.False(model.RawData.ContainsKey("fontShadow")); + Assert.Null(model.FontSize); + Assert.False(model.RawData.ContainsKey("fontSize")); + Assert.Null(model.Typography); + Assert.False(model.RawData.ContainsKey("typography")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new SubtitleOverlayTransformation + { + // Null should be interpreted as omitted for these properties + Background = null, + Color = null, + FontFamily = null, + FontOutline = null, + FontShadow = null, + FontSize = null, + Typography = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SubtitleOverlayTransformation + { + Background = "background", + Color = "color", + FontFamily = "fontFamily", + FontOutline = "fontOutline", + FontShadow = "fontShadow", + FontSize = 0, + Typography = Typography.B, + }; + + SubtitleOverlayTransformation copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class TypographyTest : TestBase +{ + [Theory] + [InlineData(Typography.B)] + [InlineData(Typography.I)] + [InlineData(Typography.BI)] + public void Validation_Works(Typography rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Typography.B)] + [InlineData(Typography.I)] + [InlineData(Typography.BI)] + public void SerializationRoundtrip_Works(Typography rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/TextOverlayTest.cs b/src/Imagekit.Tests/Models/TextOverlayTest.cs new file mode 100644 index 00000000..da810df4 --- /dev/null +++ b/src/Imagekit.Tests/Models/TextOverlayTest.cs @@ -0,0 +1,779 @@ +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class TextOverlayTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new TextOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }; + + ApiEnum expectedLayerMode = LayerMode.Multiply; + OverlayPosition expectedPosition = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }; + OverlayTiming expectedTiming = new() + { + Duration = 0, + End = 0, + Start = 0, + }; + string expectedText = "text"; + JsonElement expectedType = JsonSerializer.SerializeToElement("text"); + ApiEnum expectedEncoding = + TextOverlayTextOverlayEncoding.Auto; + List expectedTransformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ]; + + Assert.Equal(expectedLayerMode, model.LayerMode); + Assert.Equal(expectedPosition, model.Position); + Assert.Equal(expectedTiming, model.Timing); + Assert.Equal(expectedText, model.Text); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedEncoding, model.Encoding); + Assert.NotNull(model.Transformation); + Assert.Equal(expectedTransformation.Count, model.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], model.Transformation[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new TextOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new TextOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum expectedLayerMode = LayerMode.Multiply; + OverlayPosition expectedPosition = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }; + OverlayTiming expectedTiming = new() + { + Duration = 0, + End = 0, + Start = 0, + }; + string expectedText = "text"; + JsonElement expectedType = JsonSerializer.SerializeToElement("text"); + ApiEnum expectedEncoding = + TextOverlayTextOverlayEncoding.Auto; + List expectedTransformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ]; + + Assert.Equal(expectedLayerMode, deserialized.LayerMode); + Assert.Equal(expectedPosition, deserialized.Position); + Assert.Equal(expectedTiming, deserialized.Timing); + Assert.Equal(expectedText, deserialized.Text); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedEncoding, deserialized.Encoding); + Assert.NotNull(deserialized.Transformation); + Assert.Equal(expectedTransformation.Count, deserialized.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], deserialized.Transformation[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new TextOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new TextOverlay { Text = "text" }; + + Assert.Null(model.LayerMode); + Assert.False(model.RawData.ContainsKey("layerMode")); + Assert.Null(model.Position); + Assert.False(model.RawData.ContainsKey("position")); + Assert.Null(model.Timing); + Assert.False(model.RawData.ContainsKey("timing")); + Assert.Null(model.Encoding); + Assert.False(model.RawData.ContainsKey("encoding")); + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new TextOverlay { Text = "text" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new TextOverlay + { + Text = "text", + + // Null should be interpreted as omitted for these properties + LayerMode = null, + Position = null, + Timing = null, + Encoding = null, + Transformation = null, + }; + + Assert.Null(model.LayerMode); + Assert.False(model.RawData.ContainsKey("layerMode")); + Assert.Null(model.Position); + Assert.False(model.RawData.ContainsKey("position")); + Assert.Null(model.Timing); + Assert.False(model.RawData.ContainsKey("timing")); + Assert.Null(model.Encoding); + Assert.False(model.RawData.ContainsKey("encoding")); + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new TextOverlay + { + Text = "text", + + // Null should be interpreted as omitted for these properties + LayerMode = null, + Position = null, + Timing = null, + Encoding = null, + Transformation = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new TextOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }; + + TextOverlay copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class TextOverlayTextOverlayTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new TextOverlayTextOverlay + { + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }; + + string expectedText = "text"; + JsonElement expectedType = JsonSerializer.SerializeToElement("text"); + ApiEnum expectedEncoding = + TextOverlayTextOverlayEncoding.Auto; + List expectedTransformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ]; + + Assert.Equal(expectedText, model.Text); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedEncoding, model.Encoding); + Assert.NotNull(model.Transformation); + Assert.Equal(expectedTransformation.Count, model.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], model.Transformation[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new TextOverlayTextOverlay + { + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new TextOverlayTextOverlay + { + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedText = "text"; + JsonElement expectedType = JsonSerializer.SerializeToElement("text"); + ApiEnum expectedEncoding = + TextOverlayTextOverlayEncoding.Auto; + List expectedTransformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ]; + + Assert.Equal(expectedText, deserialized.Text); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedEncoding, deserialized.Encoding); + Assert.NotNull(deserialized.Transformation); + Assert.Equal(expectedTransformation.Count, deserialized.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], deserialized.Transformation[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new TextOverlayTextOverlay + { + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new TextOverlayTextOverlay { Text = "text" }; + + Assert.Null(model.Encoding); + Assert.False(model.RawData.ContainsKey("encoding")); + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new TextOverlayTextOverlay { Text = "text" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new TextOverlayTextOverlay + { + Text = "text", + + // Null should be interpreted as omitted for these properties + Encoding = null, + Transformation = null, + }; + + Assert.Null(model.Encoding); + Assert.False(model.RawData.ContainsKey("encoding")); + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new TextOverlayTextOverlay + { + Text = "text", + + // Null should be interpreted as omitted for these properties + Encoding = null, + Transformation = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new TextOverlayTextOverlay + { + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }; + + TextOverlayTextOverlay copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class TextOverlayTextOverlayEncodingTest : TestBase +{ + [Theory] + [InlineData(TextOverlayTextOverlayEncoding.Auto)] + [InlineData(TextOverlayTextOverlayEncoding.Plain)] + [InlineData(TextOverlayTextOverlayEncoding.Base64)] + public void Validation_Works(TextOverlayTextOverlayEncoding rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(TextOverlayTextOverlayEncoding.Auto)] + [InlineData(TextOverlayTextOverlayEncoding.Plain)] + [InlineData(TextOverlayTextOverlayEncoding.Base64)] + public void SerializationRoundtrip_Works(TextOverlayTextOverlayEncoding rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/TextOverlayTransformationTest.cs b/src/Imagekit.Tests/Models/TextOverlayTransformationTest.cs new file mode 100644 index 00000000..1fd10b4a --- /dev/null +++ b/src/Imagekit.Tests/Models/TextOverlayTransformationTest.cs @@ -0,0 +1,784 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class TextOverlayTransformationTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new TextOverlayTransformation + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }; + + double expectedAlpha = 1; + string expectedBackground = "background"; + ApiEnum expectedFlip = Flip.H; + string expectedFontColor = "fontColor"; + string expectedFontFamily = "fontFamily"; + FontSize expectedFontSize = 0; + ApiEnum expectedInnerAlignment = InnerAlignment.Left; + LineHeight expectedLineHeight = 0; + Padding expectedPadding = 0; + TextOverlayTransformationRadius expectedRadius = new TextOverlayTransformationRadiusMax(); + Rotation expectedRotation = 0; + string expectedTypography = "typography"; + TextOverlayTransformationWidth expectedWidth = 0; + + Assert.Equal(expectedAlpha, model.Alpha); + Assert.Equal(expectedBackground, model.Background); + Assert.Equal(expectedFlip, model.Flip); + Assert.Equal(expectedFontColor, model.FontColor); + Assert.Equal(expectedFontFamily, model.FontFamily); + Assert.Equal(expectedFontSize, model.FontSize); + Assert.Equal(expectedInnerAlignment, model.InnerAlignment); + Assert.Equal(expectedLineHeight, model.LineHeight); + Assert.Equal(expectedPadding, model.Padding); + Assert.Equal(expectedRadius, model.Radius); + Assert.Equal(expectedRotation, model.Rotation); + Assert.Equal(expectedTypography, model.Typography); + Assert.Equal(expectedWidth, model.Width); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new TextOverlayTransformation + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new TextOverlayTransformation + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + double expectedAlpha = 1; + string expectedBackground = "background"; + ApiEnum expectedFlip = Flip.H; + string expectedFontColor = "fontColor"; + string expectedFontFamily = "fontFamily"; + FontSize expectedFontSize = 0; + ApiEnum expectedInnerAlignment = InnerAlignment.Left; + LineHeight expectedLineHeight = 0; + Padding expectedPadding = 0; + TextOverlayTransformationRadius expectedRadius = new TextOverlayTransformationRadiusMax(); + Rotation expectedRotation = 0; + string expectedTypography = "typography"; + TextOverlayTransformationWidth expectedWidth = 0; + + Assert.Equal(expectedAlpha, deserialized.Alpha); + Assert.Equal(expectedBackground, deserialized.Background); + Assert.Equal(expectedFlip, deserialized.Flip); + Assert.Equal(expectedFontColor, deserialized.FontColor); + Assert.Equal(expectedFontFamily, deserialized.FontFamily); + Assert.Equal(expectedFontSize, deserialized.FontSize); + Assert.Equal(expectedInnerAlignment, deserialized.InnerAlignment); + Assert.Equal(expectedLineHeight, deserialized.LineHeight); + Assert.Equal(expectedPadding, deserialized.Padding); + Assert.Equal(expectedRadius, deserialized.Radius); + Assert.Equal(expectedRotation, deserialized.Rotation); + Assert.Equal(expectedTypography, deserialized.Typography); + Assert.Equal(expectedWidth, deserialized.Width); + } + + [Fact] + public void Validation_Works() + { + var model = new TextOverlayTransformation + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new TextOverlayTransformation { }; + + Assert.Null(model.Alpha); + Assert.False(model.RawData.ContainsKey("alpha")); + Assert.Null(model.Background); + Assert.False(model.RawData.ContainsKey("background")); + Assert.Null(model.Flip); + Assert.False(model.RawData.ContainsKey("flip")); + Assert.Null(model.FontColor); + Assert.False(model.RawData.ContainsKey("fontColor")); + Assert.Null(model.FontFamily); + Assert.False(model.RawData.ContainsKey("fontFamily")); + Assert.Null(model.FontSize); + Assert.False(model.RawData.ContainsKey("fontSize")); + Assert.Null(model.InnerAlignment); + Assert.False(model.RawData.ContainsKey("innerAlignment")); + Assert.Null(model.LineHeight); + Assert.False(model.RawData.ContainsKey("lineHeight")); + Assert.Null(model.Padding); + Assert.False(model.RawData.ContainsKey("padding")); + Assert.Null(model.Radius); + Assert.False(model.RawData.ContainsKey("radius")); + Assert.Null(model.Rotation); + Assert.False(model.RawData.ContainsKey("rotation")); + Assert.Null(model.Typography); + Assert.False(model.RawData.ContainsKey("typography")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new TextOverlayTransformation { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new TextOverlayTransformation + { + // Null should be interpreted as omitted for these properties + Alpha = null, + Background = null, + Flip = null, + FontColor = null, + FontFamily = null, + FontSize = null, + InnerAlignment = null, + LineHeight = null, + Padding = null, + Radius = null, + Rotation = null, + Typography = null, + Width = null, + }; + + Assert.Null(model.Alpha); + Assert.False(model.RawData.ContainsKey("alpha")); + Assert.Null(model.Background); + Assert.False(model.RawData.ContainsKey("background")); + Assert.Null(model.Flip); + Assert.False(model.RawData.ContainsKey("flip")); + Assert.Null(model.FontColor); + Assert.False(model.RawData.ContainsKey("fontColor")); + Assert.Null(model.FontFamily); + Assert.False(model.RawData.ContainsKey("fontFamily")); + Assert.Null(model.FontSize); + Assert.False(model.RawData.ContainsKey("fontSize")); + Assert.Null(model.InnerAlignment); + Assert.False(model.RawData.ContainsKey("innerAlignment")); + Assert.Null(model.LineHeight); + Assert.False(model.RawData.ContainsKey("lineHeight")); + Assert.Null(model.Padding); + Assert.False(model.RawData.ContainsKey("padding")); + Assert.Null(model.Radius); + Assert.False(model.RawData.ContainsKey("radius")); + Assert.Null(model.Rotation); + Assert.False(model.RawData.ContainsKey("rotation")); + Assert.Null(model.Typography); + Assert.False(model.RawData.ContainsKey("typography")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new TextOverlayTransformation + { + // Null should be interpreted as omitted for these properties + Alpha = null, + Background = null, + Flip = null, + FontColor = null, + FontFamily = null, + FontSize = null, + InnerAlignment = null, + LineHeight = null, + Padding = null, + Radius = null, + Rotation = null, + Typography = null, + Width = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new TextOverlayTransformation + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }; + + TextOverlayTransformation copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class FlipTest : TestBase +{ + [Theory] + [InlineData(Flip.H)] + [InlineData(Flip.V)] + [InlineData(Flip.HV)] + [InlineData(Flip.VH)] + public void Validation_Works(Flip rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Flip.H)] + [InlineData(Flip.V)] + [InlineData(Flip.HV)] + [InlineData(Flip.VH)] + public void SerializationRoundtrip_Works(Flip rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class FontSizeTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + FontSize value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + FontSize value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + FontSize value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + FontSize value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class InnerAlignmentTest : TestBase +{ + [Theory] + [InlineData(InnerAlignment.Left)] + [InlineData(InnerAlignment.Right)] + [InlineData(InnerAlignment.Center)] + public void Validation_Works(InnerAlignment rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(InnerAlignment.Left)] + [InlineData(InnerAlignment.Right)] + [InlineData(InnerAlignment.Center)] + public void SerializationRoundtrip_Works(InnerAlignment rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class LineHeightTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + LineHeight value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + LineHeight value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + LineHeight value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + LineHeight value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class PaddingTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + Padding value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + Padding value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + Padding value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + Padding value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class TextOverlayTransformationRadiusTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + TextOverlayTransformationRadius value = 0; + value.Validate(); + } + + [Fact] + public void MaxValidationWorks() + { + TextOverlayTransformationRadius value = new TextOverlayTransformationRadiusMax(); + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + TextOverlayTransformationRadius value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + TextOverlayTransformationRadius value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void MaxSerializationRoundtripWorks() + { + TextOverlayTransformationRadius value = new TextOverlayTransformationRadiusMax(); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + TextOverlayTransformationRadius value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class TextOverlayTransformationRadiusMaxTest : TestBase +{ + [Fact] + public void DefaultValidation_Works() + { + var constant = new TextOverlayTransformationRadiusMax(); + constant.Validate(); + } + + [Fact] + public void ValidConstantValidation_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("max"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + constant.Validate(); + } + + [Fact] + public void InvalidConstantValidationThrows_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + Assert.Throws(() => constant.Validate()); + } + + [Fact] + public void DefaultRoundtrip_Works() + { + var constant = new TextOverlayTransformationRadiusMax(); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void ValidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("max"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void InvalidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } +} + +public class RotationTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + Rotation value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + Rotation value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + Rotation value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + Rotation value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class TextOverlayTransformationWidthTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + TextOverlayTransformationWidth value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + TextOverlayTransformationWidth value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + TextOverlayTransformationWidth value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + TextOverlayTransformationWidth value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/TransformationPositionTest.cs b/src/Imagekit.Tests/Models/TransformationPositionTest.cs new file mode 100644 index 00000000..4528ed99 --- /dev/null +++ b/src/Imagekit.Tests/Models/TransformationPositionTest.cs @@ -0,0 +1,64 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class TransformationPositionTest : TestBase +{ + [Theory] + [InlineData(TransformationPosition.Path)] + [InlineData(TransformationPosition.Query)] + public void Validation_Works(TransformationPosition rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(TransformationPosition.Path)] + [InlineData(TransformationPosition.Query)] + public void SerializationRoundtrip_Works(TransformationPosition rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/TransformationTest.cs b/src/Imagekit.Tests/Models/TransformationTest.cs new file mode 100644 index 00000000..06b0ed93 --- /dev/null +++ b/src/Imagekit.Tests/Models/TransformationTest.cs @@ -0,0 +1,3381 @@ +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class TransformationTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Transformation + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }; + + string expectedAIChangeBackground = "aiChangeBackground"; + AIDropShadow expectedAIDropShadow = new AIDropShadowDefault(); + string expectedAIEdit = "aiEdit"; + ApiEnum expectedAIRemoveBackground = AIRemoveBackground.True; + ApiEnum expectedAIRemoveBackgroundExternal = + AIRemoveBackgroundExternal.True; + ApiEnum expectedAIRetouch = AIRetouch.True; + ApiEnum expectedAIUpscale = AIUpscale.True; + ApiEnum expectedAIVariation = AIVariation.True; + AspectRatio expectedAspectRatio = "4:3"; + ApiEnum expectedAudioCodec = AudioCodec.Aac; + string expectedBackground = "red"; + double expectedBlur = 10; + string expectedBorder = "5_FF0000"; + string expectedColorize = "colorize"; + bool expectedColorProfile = true; + string expectedColorReplace = "colorReplace"; + ApiEnum expectedContrastStretch = ContrastStretch.True; + ApiEnum expectedCrop = Crop.Force; + ApiEnum expectedCropMode = CropMode.PadResize; + string expectedDefaultImage = "defaultImage"; + string expectedDistort = "distort"; + double expectedDpr = 2; + TransformationDuration expectedDuration = 0; + EndOffset expectedEndOffset = 0; + ApiEnum expectedFlip = TransformationFlip.H; + string expectedFocus = "center"; + ApiEnum expectedFormat = Format.Auto; + TransformationGradient expectedGradient = new TransformationGradientDefault(); + ApiEnum expectedGrayscale = Grayscale.True; + TransformationHeight expectedHeight = 200; + bool expectedLossless = true; + bool expectedMetadata = true; + string expectedNamed = "named"; + double expectedOpacity = 0; + bool expectedOriginal = true; + Overlay expectedOverlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }; + Page expectedPage = 0; + bool expectedProgressive = true; + double expectedQuality = 80; + TransformationRadius expectedRadius = 20; + string expectedRaw = "raw"; + TransformationRotation expectedRotation = 90; + Shadow expectedShadow = new ShadowDefault(); + Sharpen expectedSharpen = new SharpenDefault(); + StartOffset expectedStartOffset = 0; + List> expectedStreamingResolutions = + [ + StreamingResolution.V240, + ]; + Trim expectedTrim = new TrimDefault(); + UnsharpMask expectedUnsharpMask = new UnsharpMaskDefault(); + ApiEnum expectedVideoCodec = VideoCodec.H264; + TransformationWidth expectedWidth = 300; + TransformationX expectedX = 0; + TransformationXCenter expectedXCenter = 0; + TransformationY expectedY = 0; + TransformationYCenter expectedYCenter = 0; + double expectedZoom = 0; + + Assert.Equal(expectedAIChangeBackground, model.AIChangeBackground); + Assert.Equal(expectedAIDropShadow, model.AIDropShadow); + Assert.Equal(expectedAIEdit, model.AIEdit); + Assert.Equal(expectedAIRemoveBackground, model.AIRemoveBackground); + Assert.Equal(expectedAIRemoveBackgroundExternal, model.AIRemoveBackgroundExternal); + Assert.Equal(expectedAIRetouch, model.AIRetouch); + Assert.Equal(expectedAIUpscale, model.AIUpscale); + Assert.Equal(expectedAIVariation, model.AIVariation); + Assert.Equal(expectedAspectRatio, model.AspectRatio); + Assert.Equal(expectedAudioCodec, model.AudioCodec); + Assert.Equal(expectedBackground, model.Background); + Assert.Equal(expectedBlur, model.Blur); + Assert.Equal(expectedBorder, model.Border); + Assert.Equal(expectedColorize, model.Colorize); + Assert.Equal(expectedColorProfile, model.ColorProfile); + Assert.Equal(expectedColorReplace, model.ColorReplace); + Assert.Equal(expectedContrastStretch, model.ContrastStretch); + Assert.Equal(expectedCrop, model.Crop); + Assert.Equal(expectedCropMode, model.CropMode); + Assert.Equal(expectedDefaultImage, model.DefaultImage); + Assert.Equal(expectedDistort, model.Distort); + Assert.Equal(expectedDpr, model.Dpr); + Assert.Equal(expectedDuration, model.Duration); + Assert.Equal(expectedEndOffset, model.EndOffset); + Assert.Equal(expectedFlip, model.Flip); + Assert.Equal(expectedFocus, model.Focus); + Assert.Equal(expectedFormat, model.Format); + Assert.Equal(expectedGradient, model.Gradient); + Assert.Equal(expectedGrayscale, model.Grayscale); + Assert.Equal(expectedHeight, model.Height); + Assert.Equal(expectedLossless, model.Lossless); + Assert.Equal(expectedMetadata, model.Metadata); + Assert.Equal(expectedNamed, model.Named); + Assert.Equal(expectedOpacity, model.Opacity); + Assert.Equal(expectedOriginal, model.Original); + Assert.Equal(expectedOverlay, model.Overlay); + Assert.Equal(expectedPage, model.Page); + Assert.Equal(expectedProgressive, model.Progressive); + Assert.Equal(expectedQuality, model.Quality); + Assert.Equal(expectedRadius, model.Radius); + Assert.Equal(expectedRaw, model.Raw); + Assert.Equal(expectedRotation, model.Rotation); + Assert.Equal(expectedShadow, model.Shadow); + Assert.Equal(expectedSharpen, model.Sharpen); + Assert.Equal(expectedStartOffset, model.StartOffset); + Assert.NotNull(model.StreamingResolutions); + Assert.Equal(expectedStreamingResolutions.Count, model.StreamingResolutions.Count); + for (int i = 0; i < expectedStreamingResolutions.Count; i++) + { + Assert.Equal(expectedStreamingResolutions[i], model.StreamingResolutions[i]); + } + Assert.Equal(expectedTrim, model.Trim); + Assert.Equal(expectedUnsharpMask, model.UnsharpMask); + Assert.Equal(expectedVideoCodec, model.VideoCodec); + Assert.Equal(expectedWidth, model.Width); + Assert.Equal(expectedX, model.X); + Assert.Equal(expectedXCenter, model.XCenter); + Assert.Equal(expectedY, model.Y); + Assert.Equal(expectedYCenter, model.YCenter); + Assert.Equal(expectedZoom, model.Zoom); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Transformation + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Transformation + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedAIChangeBackground = "aiChangeBackground"; + AIDropShadow expectedAIDropShadow = new AIDropShadowDefault(); + string expectedAIEdit = "aiEdit"; + ApiEnum expectedAIRemoveBackground = AIRemoveBackground.True; + ApiEnum expectedAIRemoveBackgroundExternal = + AIRemoveBackgroundExternal.True; + ApiEnum expectedAIRetouch = AIRetouch.True; + ApiEnum expectedAIUpscale = AIUpscale.True; + ApiEnum expectedAIVariation = AIVariation.True; + AspectRatio expectedAspectRatio = "4:3"; + ApiEnum expectedAudioCodec = AudioCodec.Aac; + string expectedBackground = "red"; + double expectedBlur = 10; + string expectedBorder = "5_FF0000"; + string expectedColorize = "colorize"; + bool expectedColorProfile = true; + string expectedColorReplace = "colorReplace"; + ApiEnum expectedContrastStretch = ContrastStretch.True; + ApiEnum expectedCrop = Crop.Force; + ApiEnum expectedCropMode = CropMode.PadResize; + string expectedDefaultImage = "defaultImage"; + string expectedDistort = "distort"; + double expectedDpr = 2; + TransformationDuration expectedDuration = 0; + EndOffset expectedEndOffset = 0; + ApiEnum expectedFlip = TransformationFlip.H; + string expectedFocus = "center"; + ApiEnum expectedFormat = Format.Auto; + TransformationGradient expectedGradient = new TransformationGradientDefault(); + ApiEnum expectedGrayscale = Grayscale.True; + TransformationHeight expectedHeight = 200; + bool expectedLossless = true; + bool expectedMetadata = true; + string expectedNamed = "named"; + double expectedOpacity = 0; + bool expectedOriginal = true; + Overlay expectedOverlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }; + Page expectedPage = 0; + bool expectedProgressive = true; + double expectedQuality = 80; + TransformationRadius expectedRadius = 20; + string expectedRaw = "raw"; + TransformationRotation expectedRotation = 90; + Shadow expectedShadow = new ShadowDefault(); + Sharpen expectedSharpen = new SharpenDefault(); + StartOffset expectedStartOffset = 0; + List> expectedStreamingResolutions = + [ + StreamingResolution.V240, + ]; + Trim expectedTrim = new TrimDefault(); + UnsharpMask expectedUnsharpMask = new UnsharpMaskDefault(); + ApiEnum expectedVideoCodec = VideoCodec.H264; + TransformationWidth expectedWidth = 300; + TransformationX expectedX = 0; + TransformationXCenter expectedXCenter = 0; + TransformationY expectedY = 0; + TransformationYCenter expectedYCenter = 0; + double expectedZoom = 0; + + Assert.Equal(expectedAIChangeBackground, deserialized.AIChangeBackground); + Assert.Equal(expectedAIDropShadow, deserialized.AIDropShadow); + Assert.Equal(expectedAIEdit, deserialized.AIEdit); + Assert.Equal(expectedAIRemoveBackground, deserialized.AIRemoveBackground); + Assert.Equal(expectedAIRemoveBackgroundExternal, deserialized.AIRemoveBackgroundExternal); + Assert.Equal(expectedAIRetouch, deserialized.AIRetouch); + Assert.Equal(expectedAIUpscale, deserialized.AIUpscale); + Assert.Equal(expectedAIVariation, deserialized.AIVariation); + Assert.Equal(expectedAspectRatio, deserialized.AspectRatio); + Assert.Equal(expectedAudioCodec, deserialized.AudioCodec); + Assert.Equal(expectedBackground, deserialized.Background); + Assert.Equal(expectedBlur, deserialized.Blur); + Assert.Equal(expectedBorder, deserialized.Border); + Assert.Equal(expectedColorize, deserialized.Colorize); + Assert.Equal(expectedColorProfile, deserialized.ColorProfile); + Assert.Equal(expectedColorReplace, deserialized.ColorReplace); + Assert.Equal(expectedContrastStretch, deserialized.ContrastStretch); + Assert.Equal(expectedCrop, deserialized.Crop); + Assert.Equal(expectedCropMode, deserialized.CropMode); + Assert.Equal(expectedDefaultImage, deserialized.DefaultImage); + Assert.Equal(expectedDistort, deserialized.Distort); + Assert.Equal(expectedDpr, deserialized.Dpr); + Assert.Equal(expectedDuration, deserialized.Duration); + Assert.Equal(expectedEndOffset, deserialized.EndOffset); + Assert.Equal(expectedFlip, deserialized.Flip); + Assert.Equal(expectedFocus, deserialized.Focus); + Assert.Equal(expectedFormat, deserialized.Format); + Assert.Equal(expectedGradient, deserialized.Gradient); + Assert.Equal(expectedGrayscale, deserialized.Grayscale); + Assert.Equal(expectedHeight, deserialized.Height); + Assert.Equal(expectedLossless, deserialized.Lossless); + Assert.Equal(expectedMetadata, deserialized.Metadata); + Assert.Equal(expectedNamed, deserialized.Named); + Assert.Equal(expectedOpacity, deserialized.Opacity); + Assert.Equal(expectedOriginal, deserialized.Original); + Assert.Equal(expectedOverlay, deserialized.Overlay); + Assert.Equal(expectedPage, deserialized.Page); + Assert.Equal(expectedProgressive, deserialized.Progressive); + Assert.Equal(expectedQuality, deserialized.Quality); + Assert.Equal(expectedRadius, deserialized.Radius); + Assert.Equal(expectedRaw, deserialized.Raw); + Assert.Equal(expectedRotation, deserialized.Rotation); + Assert.Equal(expectedShadow, deserialized.Shadow); + Assert.Equal(expectedSharpen, deserialized.Sharpen); + Assert.Equal(expectedStartOffset, deserialized.StartOffset); + Assert.NotNull(deserialized.StreamingResolutions); + Assert.Equal(expectedStreamingResolutions.Count, deserialized.StreamingResolutions.Count); + for (int i = 0; i < expectedStreamingResolutions.Count; i++) + { + Assert.Equal(expectedStreamingResolutions[i], deserialized.StreamingResolutions[i]); + } + Assert.Equal(expectedTrim, deserialized.Trim); + Assert.Equal(expectedUnsharpMask, deserialized.UnsharpMask); + Assert.Equal(expectedVideoCodec, deserialized.VideoCodec); + Assert.Equal(expectedWidth, deserialized.Width); + Assert.Equal(expectedX, deserialized.X); + Assert.Equal(expectedXCenter, deserialized.XCenter); + Assert.Equal(expectedY, deserialized.Y); + Assert.Equal(expectedYCenter, deserialized.YCenter); + Assert.Equal(expectedZoom, deserialized.Zoom); + } + + [Fact] + public void Validation_Works() + { + var model = new Transformation + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Transformation { }; + + Assert.Null(model.AIChangeBackground); + Assert.False(model.RawData.ContainsKey("aiChangeBackground")); + Assert.Null(model.AIDropShadow); + Assert.False(model.RawData.ContainsKey("aiDropShadow")); + Assert.Null(model.AIEdit); + Assert.False(model.RawData.ContainsKey("aiEdit")); + Assert.Null(model.AIRemoveBackground); + Assert.False(model.RawData.ContainsKey("aiRemoveBackground")); + Assert.Null(model.AIRemoveBackgroundExternal); + Assert.False(model.RawData.ContainsKey("aiRemoveBackgroundExternal")); + Assert.Null(model.AIRetouch); + Assert.False(model.RawData.ContainsKey("aiRetouch")); + Assert.Null(model.AIUpscale); + Assert.False(model.RawData.ContainsKey("aiUpscale")); + Assert.Null(model.AIVariation); + Assert.False(model.RawData.ContainsKey("aiVariation")); + Assert.Null(model.AspectRatio); + Assert.False(model.RawData.ContainsKey("aspectRatio")); + Assert.Null(model.AudioCodec); + Assert.False(model.RawData.ContainsKey("audioCodec")); + Assert.Null(model.Background); + Assert.False(model.RawData.ContainsKey("background")); + Assert.Null(model.Blur); + Assert.False(model.RawData.ContainsKey("blur")); + Assert.Null(model.Border); + Assert.False(model.RawData.ContainsKey("border")); + Assert.Null(model.Colorize); + Assert.False(model.RawData.ContainsKey("colorize")); + Assert.Null(model.ColorProfile); + Assert.False(model.RawData.ContainsKey("colorProfile")); + Assert.Null(model.ColorReplace); + Assert.False(model.RawData.ContainsKey("colorReplace")); + Assert.Null(model.ContrastStretch); + Assert.False(model.RawData.ContainsKey("contrastStretch")); + Assert.Null(model.Crop); + Assert.False(model.RawData.ContainsKey("crop")); + Assert.Null(model.CropMode); + Assert.False(model.RawData.ContainsKey("cropMode")); + Assert.Null(model.DefaultImage); + Assert.False(model.RawData.ContainsKey("defaultImage")); + Assert.Null(model.Distort); + Assert.False(model.RawData.ContainsKey("distort")); + Assert.Null(model.Dpr); + Assert.False(model.RawData.ContainsKey("dpr")); + Assert.Null(model.Duration); + Assert.False(model.RawData.ContainsKey("duration")); + Assert.Null(model.EndOffset); + Assert.False(model.RawData.ContainsKey("endOffset")); + Assert.Null(model.Flip); + Assert.False(model.RawData.ContainsKey("flip")); + Assert.Null(model.Focus); + Assert.False(model.RawData.ContainsKey("focus")); + Assert.Null(model.Format); + Assert.False(model.RawData.ContainsKey("format")); + Assert.Null(model.Gradient); + Assert.False(model.RawData.ContainsKey("gradient")); + Assert.Null(model.Grayscale); + Assert.False(model.RawData.ContainsKey("grayscale")); + Assert.Null(model.Height); + Assert.False(model.RawData.ContainsKey("height")); + Assert.Null(model.Lossless); + Assert.False(model.RawData.ContainsKey("lossless")); + Assert.Null(model.Metadata); + Assert.False(model.RawData.ContainsKey("metadata")); + Assert.Null(model.Named); + Assert.False(model.RawData.ContainsKey("named")); + Assert.Null(model.Opacity); + Assert.False(model.RawData.ContainsKey("opacity")); + Assert.Null(model.Original); + Assert.False(model.RawData.ContainsKey("original")); + Assert.Null(model.Overlay); + Assert.False(model.RawData.ContainsKey("overlay")); + Assert.Null(model.Page); + Assert.False(model.RawData.ContainsKey("page")); + Assert.Null(model.Progressive); + Assert.False(model.RawData.ContainsKey("progressive")); + Assert.Null(model.Quality); + Assert.False(model.RawData.ContainsKey("quality")); + Assert.Null(model.Radius); + Assert.False(model.RawData.ContainsKey("radius")); + Assert.Null(model.Raw); + Assert.False(model.RawData.ContainsKey("raw")); + Assert.Null(model.Rotation); + Assert.False(model.RawData.ContainsKey("rotation")); + Assert.Null(model.Shadow); + Assert.False(model.RawData.ContainsKey("shadow")); + Assert.Null(model.Sharpen); + Assert.False(model.RawData.ContainsKey("sharpen")); + Assert.Null(model.StartOffset); + Assert.False(model.RawData.ContainsKey("startOffset")); + Assert.Null(model.StreamingResolutions); + Assert.False(model.RawData.ContainsKey("streamingResolutions")); + Assert.Null(model.Trim); + Assert.False(model.RawData.ContainsKey("trim")); + Assert.Null(model.UnsharpMask); + Assert.False(model.RawData.ContainsKey("unsharpMask")); + Assert.Null(model.VideoCodec); + Assert.False(model.RawData.ContainsKey("videoCodec")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + Assert.Null(model.X); + Assert.False(model.RawData.ContainsKey("x")); + Assert.Null(model.XCenter); + Assert.False(model.RawData.ContainsKey("xCenter")); + Assert.Null(model.Y); + Assert.False(model.RawData.ContainsKey("y")); + Assert.Null(model.YCenter); + Assert.False(model.RawData.ContainsKey("yCenter")); + Assert.Null(model.Zoom); + Assert.False(model.RawData.ContainsKey("zoom")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Transformation { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Transformation + { + // Null should be interpreted as omitted for these properties + AIChangeBackground = null, + AIDropShadow = null, + AIEdit = null, + AIRemoveBackground = null, + AIRemoveBackgroundExternal = null, + AIRetouch = null, + AIUpscale = null, + AIVariation = null, + AspectRatio = null, + AudioCodec = null, + Background = null, + Blur = null, + Border = null, + Colorize = null, + ColorProfile = null, + ColorReplace = null, + ContrastStretch = null, + Crop = null, + CropMode = null, + DefaultImage = null, + Distort = null, + Dpr = null, + Duration = null, + EndOffset = null, + Flip = null, + Focus = null, + Format = null, + Gradient = null, + Grayscale = null, + Height = null, + Lossless = null, + Metadata = null, + Named = null, + Opacity = null, + Original = null, + Overlay = null, + Page = null, + Progressive = null, + Quality = null, + Radius = null, + Raw = null, + Rotation = null, + Shadow = null, + Sharpen = null, + StartOffset = null, + StreamingResolutions = null, + Trim = null, + UnsharpMask = null, + VideoCodec = null, + Width = null, + X = null, + XCenter = null, + Y = null, + YCenter = null, + Zoom = null, + }; + + Assert.Null(model.AIChangeBackground); + Assert.False(model.RawData.ContainsKey("aiChangeBackground")); + Assert.Null(model.AIDropShadow); + Assert.False(model.RawData.ContainsKey("aiDropShadow")); + Assert.Null(model.AIEdit); + Assert.False(model.RawData.ContainsKey("aiEdit")); + Assert.Null(model.AIRemoveBackground); + Assert.False(model.RawData.ContainsKey("aiRemoveBackground")); + Assert.Null(model.AIRemoveBackgroundExternal); + Assert.False(model.RawData.ContainsKey("aiRemoveBackgroundExternal")); + Assert.Null(model.AIRetouch); + Assert.False(model.RawData.ContainsKey("aiRetouch")); + Assert.Null(model.AIUpscale); + Assert.False(model.RawData.ContainsKey("aiUpscale")); + Assert.Null(model.AIVariation); + Assert.False(model.RawData.ContainsKey("aiVariation")); + Assert.Null(model.AspectRatio); + Assert.False(model.RawData.ContainsKey("aspectRatio")); + Assert.Null(model.AudioCodec); + Assert.False(model.RawData.ContainsKey("audioCodec")); + Assert.Null(model.Background); + Assert.False(model.RawData.ContainsKey("background")); + Assert.Null(model.Blur); + Assert.False(model.RawData.ContainsKey("blur")); + Assert.Null(model.Border); + Assert.False(model.RawData.ContainsKey("border")); + Assert.Null(model.Colorize); + Assert.False(model.RawData.ContainsKey("colorize")); + Assert.Null(model.ColorProfile); + Assert.False(model.RawData.ContainsKey("colorProfile")); + Assert.Null(model.ColorReplace); + Assert.False(model.RawData.ContainsKey("colorReplace")); + Assert.Null(model.ContrastStretch); + Assert.False(model.RawData.ContainsKey("contrastStretch")); + Assert.Null(model.Crop); + Assert.False(model.RawData.ContainsKey("crop")); + Assert.Null(model.CropMode); + Assert.False(model.RawData.ContainsKey("cropMode")); + Assert.Null(model.DefaultImage); + Assert.False(model.RawData.ContainsKey("defaultImage")); + Assert.Null(model.Distort); + Assert.False(model.RawData.ContainsKey("distort")); + Assert.Null(model.Dpr); + Assert.False(model.RawData.ContainsKey("dpr")); + Assert.Null(model.Duration); + Assert.False(model.RawData.ContainsKey("duration")); + Assert.Null(model.EndOffset); + Assert.False(model.RawData.ContainsKey("endOffset")); + Assert.Null(model.Flip); + Assert.False(model.RawData.ContainsKey("flip")); + Assert.Null(model.Focus); + Assert.False(model.RawData.ContainsKey("focus")); + Assert.Null(model.Format); + Assert.False(model.RawData.ContainsKey("format")); + Assert.Null(model.Gradient); + Assert.False(model.RawData.ContainsKey("gradient")); + Assert.Null(model.Grayscale); + Assert.False(model.RawData.ContainsKey("grayscale")); + Assert.Null(model.Height); + Assert.False(model.RawData.ContainsKey("height")); + Assert.Null(model.Lossless); + Assert.False(model.RawData.ContainsKey("lossless")); + Assert.Null(model.Metadata); + Assert.False(model.RawData.ContainsKey("metadata")); + Assert.Null(model.Named); + Assert.False(model.RawData.ContainsKey("named")); + Assert.Null(model.Opacity); + Assert.False(model.RawData.ContainsKey("opacity")); + Assert.Null(model.Original); + Assert.False(model.RawData.ContainsKey("original")); + Assert.Null(model.Overlay); + Assert.False(model.RawData.ContainsKey("overlay")); + Assert.Null(model.Page); + Assert.False(model.RawData.ContainsKey("page")); + Assert.Null(model.Progressive); + Assert.False(model.RawData.ContainsKey("progressive")); + Assert.Null(model.Quality); + Assert.False(model.RawData.ContainsKey("quality")); + Assert.Null(model.Radius); + Assert.False(model.RawData.ContainsKey("radius")); + Assert.Null(model.Raw); + Assert.False(model.RawData.ContainsKey("raw")); + Assert.Null(model.Rotation); + Assert.False(model.RawData.ContainsKey("rotation")); + Assert.Null(model.Shadow); + Assert.False(model.RawData.ContainsKey("shadow")); + Assert.Null(model.Sharpen); + Assert.False(model.RawData.ContainsKey("sharpen")); + Assert.Null(model.StartOffset); + Assert.False(model.RawData.ContainsKey("startOffset")); + Assert.Null(model.StreamingResolutions); + Assert.False(model.RawData.ContainsKey("streamingResolutions")); + Assert.Null(model.Trim); + Assert.False(model.RawData.ContainsKey("trim")); + Assert.Null(model.UnsharpMask); + Assert.False(model.RawData.ContainsKey("unsharpMask")); + Assert.Null(model.VideoCodec); + Assert.False(model.RawData.ContainsKey("videoCodec")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + Assert.Null(model.X); + Assert.False(model.RawData.ContainsKey("x")); + Assert.Null(model.XCenter); + Assert.False(model.RawData.ContainsKey("xCenter")); + Assert.Null(model.Y); + Assert.False(model.RawData.ContainsKey("y")); + Assert.Null(model.YCenter); + Assert.False(model.RawData.ContainsKey("yCenter")); + Assert.Null(model.Zoom); + Assert.False(model.RawData.ContainsKey("zoom")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Transformation + { + // Null should be interpreted as omitted for these properties + AIChangeBackground = null, + AIDropShadow = null, + AIEdit = null, + AIRemoveBackground = null, + AIRemoveBackgroundExternal = null, + AIRetouch = null, + AIUpscale = null, + AIVariation = null, + AspectRatio = null, + AudioCodec = null, + Background = null, + Blur = null, + Border = null, + Colorize = null, + ColorProfile = null, + ColorReplace = null, + ContrastStretch = null, + Crop = null, + CropMode = null, + DefaultImage = null, + Distort = null, + Dpr = null, + Duration = null, + EndOffset = null, + Flip = null, + Focus = null, + Format = null, + Gradient = null, + Grayscale = null, + Height = null, + Lossless = null, + Metadata = null, + Named = null, + Opacity = null, + Original = null, + Overlay = null, + Page = null, + Progressive = null, + Quality = null, + Radius = null, + Raw = null, + Rotation = null, + Shadow = null, + Sharpen = null, + StartOffset = null, + StreamingResolutions = null, + Trim = null, + UnsharpMask = null, + VideoCodec = null, + Width = null, + X = null, + XCenter = null, + Y = null, + YCenter = null, + Zoom = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Transformation + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }; + + Transformation copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class AIDropShadowTest : TestBase +{ + [Fact] + public void DefaultValidationWorks() + { + AIDropShadow value = new AIDropShadowDefault(); + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + AIDropShadow value = "string"; + value.Validate(); + } + + [Fact] + public void DefaultSerializationRoundtripWorks() + { + AIDropShadow value = new AIDropShadowDefault(); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + AIDropShadow value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class AIDropShadowDefaultTest : TestBase +{ + [Fact] + public void DefaultValidation_Works() + { + var constant = new AIDropShadowDefault(); + constant.Validate(); + } + + [Fact] + public void ValidConstantValidation_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement(true), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + constant.Validate(); + } + + [Fact] + public void InvalidConstantValidationThrows_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + Assert.Throws(() => constant.Validate()); + } + + [Fact] + public void DefaultRoundtrip_Works() + { + var constant = new AIDropShadowDefault(); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void ValidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement(true), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void InvalidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } +} + +public class AIRemoveBackgroundTest : TestBase +{ + [Theory] + [InlineData(AIRemoveBackground.True)] + public void Validation_Works(AIRemoveBackground rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(AIRemoveBackground.True)] + public void SerializationRoundtrip_Works(AIRemoveBackground rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class AIRemoveBackgroundExternalTest : TestBase +{ + [Theory] + [InlineData(AIRemoveBackgroundExternal.True)] + public void Validation_Works(AIRemoveBackgroundExternal rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(AIRemoveBackgroundExternal.True)] + public void SerializationRoundtrip_Works(AIRemoveBackgroundExternal rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class AIRetouchTest : TestBase +{ + [Theory] + [InlineData(AIRetouch.True)] + public void Validation_Works(AIRetouch rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(AIRetouch.True)] + public void SerializationRoundtrip_Works(AIRetouch rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class AIUpscaleTest : TestBase +{ + [Theory] + [InlineData(AIUpscale.True)] + public void Validation_Works(AIUpscale rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(AIUpscale.True)] + public void SerializationRoundtrip_Works(AIUpscale rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class AIVariationTest : TestBase +{ + [Theory] + [InlineData(AIVariation.True)] + public void Validation_Works(AIVariation rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(AIVariation.True)] + public void SerializationRoundtrip_Works(AIVariation rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class AspectRatioTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + AspectRatio value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + AspectRatio value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + AspectRatio value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + AspectRatio value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class AudioCodecTest : TestBase +{ + [Theory] + [InlineData(AudioCodec.Aac)] + [InlineData(AudioCodec.Opus)] + [InlineData(AudioCodec.None)] + public void Validation_Works(AudioCodec rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(AudioCodec.Aac)] + [InlineData(AudioCodec.Opus)] + [InlineData(AudioCodec.None)] + public void SerializationRoundtrip_Works(AudioCodec rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class ContrastStretchTest : TestBase +{ + [Theory] + [InlineData(ContrastStretch.True)] + public void Validation_Works(ContrastStretch rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(ContrastStretch.True)] + public void SerializationRoundtrip_Works(ContrastStretch rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class CropTest : TestBase +{ + [Theory] + [InlineData(Crop.Force)] + [InlineData(Crop.AtMax)] + [InlineData(Crop.AtMaxEnlarge)] + [InlineData(Crop.AtLeast)] + [InlineData(Crop.MaintainRatio)] + [InlineData(Crop.MaintainRatioNoEnlarge)] + public void Validation_Works(Crop rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Crop.Force)] + [InlineData(Crop.AtMax)] + [InlineData(Crop.AtMaxEnlarge)] + [InlineData(Crop.AtLeast)] + [InlineData(Crop.MaintainRatio)] + [InlineData(Crop.MaintainRatioNoEnlarge)] + public void SerializationRoundtrip_Works(Crop rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class CropModeTest : TestBase +{ + [Theory] + [InlineData(CropMode.PadResize)] + [InlineData(CropMode.Extract)] + [InlineData(CropMode.PadExtract)] + [InlineData(CropMode.PadResizeNoEnlarge)] + [InlineData(CropMode.PadExtractNoShrink)] + public void Validation_Works(CropMode rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(CropMode.PadResize)] + [InlineData(CropMode.Extract)] + [InlineData(CropMode.PadExtract)] + [InlineData(CropMode.PadResizeNoEnlarge)] + [InlineData(CropMode.PadExtractNoShrink)] + public void SerializationRoundtrip_Works(CropMode rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class TransformationDurationTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + TransformationDuration value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + TransformationDuration value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + TransformationDuration value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + TransformationDuration value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class EndOffsetTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + EndOffset value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + EndOffset value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + EndOffset value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + EndOffset value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class TransformationFlipTest : TestBase +{ + [Theory] + [InlineData(TransformationFlip.H)] + [InlineData(TransformationFlip.V)] + [InlineData(TransformationFlip.HV)] + [InlineData(TransformationFlip.VH)] + public void Validation_Works(TransformationFlip rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(TransformationFlip.H)] + [InlineData(TransformationFlip.V)] + [InlineData(TransformationFlip.HV)] + [InlineData(TransformationFlip.VH)] + public void SerializationRoundtrip_Works(TransformationFlip rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class FormatTest : TestBase +{ + [Theory] + [InlineData(Format.Auto)] + [InlineData(Format.Webp)] + [InlineData(Format.Jpg)] + [InlineData(Format.Jpeg)] + [InlineData(Format.Png)] + [InlineData(Format.Gif)] + [InlineData(Format.Svg)] + [InlineData(Format.Mp4)] + [InlineData(Format.Webm)] + [InlineData(Format.Avif)] + [InlineData(Format.Orig)] + public void Validation_Works(Format rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Format.Auto)] + [InlineData(Format.Webp)] + [InlineData(Format.Jpg)] + [InlineData(Format.Jpeg)] + [InlineData(Format.Png)] + [InlineData(Format.Gif)] + [InlineData(Format.Svg)] + [InlineData(Format.Mp4)] + [InlineData(Format.Webm)] + [InlineData(Format.Avif)] + [InlineData(Format.Orig)] + public void SerializationRoundtrip_Works(Format rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class TransformationGradientTest : TestBase +{ + [Fact] + public void DefaultValidationWorks() + { + TransformationGradient value = new TransformationGradientDefault(); + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + TransformationGradient value = "string"; + value.Validate(); + } + + [Fact] + public void DefaultSerializationRoundtripWorks() + { + TransformationGradient value = new TransformationGradientDefault(); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + TransformationGradient value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class TransformationGradientDefaultTest : TestBase +{ + [Fact] + public void DefaultValidation_Works() + { + var constant = new TransformationGradientDefault(); + constant.Validate(); + } + + [Fact] + public void ValidConstantValidation_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement(true), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + constant.Validate(); + } + + [Fact] + public void InvalidConstantValidationThrows_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + Assert.Throws(() => constant.Validate()); + } + + [Fact] + public void DefaultRoundtrip_Works() + { + var constant = new TransformationGradientDefault(); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void ValidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement(true), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void InvalidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } +} + +public class GrayscaleTest : TestBase +{ + [Theory] + [InlineData(Grayscale.True)] + public void Validation_Works(Grayscale rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Grayscale.True)] + public void SerializationRoundtrip_Works(Grayscale rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class TransformationHeightTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + TransformationHeight value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + TransformationHeight value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + TransformationHeight value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + TransformationHeight value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class PageTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + Page value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + Page value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + Page value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + Page value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class TransformationRadiusTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + TransformationRadius value = 0; + value.Validate(); + } + + [Fact] + public void MaxValidationWorks() + { + TransformationRadius value = new TransformationRadiusMax(); + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + TransformationRadius value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + TransformationRadius value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void MaxSerializationRoundtripWorks() + { + TransformationRadius value = new TransformationRadiusMax(); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + TransformationRadius value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class TransformationRadiusMaxTest : TestBase +{ + [Fact] + public void DefaultValidation_Works() + { + var constant = new TransformationRadiusMax(); + constant.Validate(); + } + + [Fact] + public void ValidConstantValidation_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("max"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + constant.Validate(); + } + + [Fact] + public void InvalidConstantValidationThrows_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + Assert.Throws(() => constant.Validate()); + } + + [Fact] + public void DefaultRoundtrip_Works() + { + var constant = new TransformationRadiusMax(); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void ValidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("max"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void InvalidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } +} + +public class TransformationRotationTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + TransformationRotation value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + TransformationRotation value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + TransformationRotation value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + TransformationRotation value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class ShadowTest : TestBase +{ + [Fact] + public void DefaultValidationWorks() + { + Shadow value = new ShadowDefault(); + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + Shadow value = "string"; + value.Validate(); + } + + [Fact] + public void DefaultSerializationRoundtripWorks() + { + Shadow value = new ShadowDefault(); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + Shadow value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class ShadowDefaultTest : TestBase +{ + [Fact] + public void DefaultValidation_Works() + { + var constant = new ShadowDefault(); + constant.Validate(); + } + + [Fact] + public void ValidConstantValidation_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement(true), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + constant.Validate(); + } + + [Fact] + public void InvalidConstantValidationThrows_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + Assert.Throws(() => constant.Validate()); + } + + [Fact] + public void DefaultRoundtrip_Works() + { + var constant = new ShadowDefault(); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void ValidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement(true), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void InvalidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } +} + +public class SharpenTest : TestBase +{ + [Fact] + public void DefaultValidationWorks() + { + Sharpen value = new SharpenDefault(); + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + Sharpen value = 0; + value.Validate(); + } + + [Fact] + public void DefaultSerializationRoundtripWorks() + { + Sharpen value = new SharpenDefault(); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + Sharpen value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class SharpenDefaultTest : TestBase +{ + [Fact] + public void DefaultValidation_Works() + { + var constant = new SharpenDefault(); + constant.Validate(); + } + + [Fact] + public void ValidConstantValidation_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement(true), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + constant.Validate(); + } + + [Fact] + public void InvalidConstantValidationThrows_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + Assert.Throws(() => constant.Validate()); + } + + [Fact] + public void DefaultRoundtrip_Works() + { + var constant = new SharpenDefault(); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void ValidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement(true), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void InvalidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } +} + +public class StartOffsetTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + StartOffset value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + StartOffset value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + StartOffset value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + StartOffset value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class TrimTest : TestBase +{ + [Fact] + public void DefaultValidationWorks() + { + Trim value = new TrimDefault(); + value.Validate(); + } + + [Fact] + public void DoubleValidationWorks() + { + Trim value = 0; + value.Validate(); + } + + [Fact] + public void DefaultSerializationRoundtripWorks() + { + Trim value = new TrimDefault(); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + Trim value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class TrimDefaultTest : TestBase +{ + [Fact] + public void DefaultValidation_Works() + { + var constant = new TrimDefault(); + constant.Validate(); + } + + [Fact] + public void ValidConstantValidation_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement(true), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + constant.Validate(); + } + + [Fact] + public void InvalidConstantValidationThrows_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + Assert.Throws(() => constant.Validate()); + } + + [Fact] + public void DefaultRoundtrip_Works() + { + var constant = new TrimDefault(); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void ValidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement(true), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void InvalidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } +} + +public class UnsharpMaskTest : TestBase +{ + [Fact] + public void DefaultValidationWorks() + { + UnsharpMask value = new UnsharpMaskDefault(); + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + UnsharpMask value = "string"; + value.Validate(); + } + + [Fact] + public void DefaultSerializationRoundtripWorks() + { + UnsharpMask value = new UnsharpMaskDefault(); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + UnsharpMask value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class UnsharpMaskDefaultTest : TestBase +{ + [Fact] + public void DefaultValidation_Works() + { + var constant = new UnsharpMaskDefault(); + constant.Validate(); + } + + [Fact] + public void ValidConstantValidation_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement(true), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + constant.Validate(); + } + + [Fact] + public void InvalidConstantValidationThrows_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(constant); + Assert.Throws(() => constant.Validate()); + } + + [Fact] + public void DefaultRoundtrip_Works() + { + var constant = new UnsharpMaskDefault(); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void ValidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement(true), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } + + [Fact] + public void InvalidConstantRoundtrip_Works() + { + var constant = JsonSerializer.Deserialize( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string element = JsonSerializer.Serialize(constant, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(constant, deserialized); + } +} + +public class VideoCodecTest : TestBase +{ + [Theory] + [InlineData(VideoCodec.H264)] + [InlineData(VideoCodec.Vp9)] + [InlineData(VideoCodec.Av1)] + [InlineData(VideoCodec.None)] + public void Validation_Works(VideoCodec rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(VideoCodec.H264)] + [InlineData(VideoCodec.Vp9)] + [InlineData(VideoCodec.Av1)] + [InlineData(VideoCodec.None)] + public void SerializationRoundtrip_Works(VideoCodec rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class TransformationWidthTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + TransformationWidth value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + TransformationWidth value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + TransformationWidth value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + TransformationWidth value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class TransformationXTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + TransformationX value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + TransformationX value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + TransformationX value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + TransformationX value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class TransformationXCenterTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + TransformationXCenter value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + TransformationXCenter value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + TransformationXCenter value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + TransformationXCenter value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class TransformationYTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + TransformationY value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + TransformationY value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + TransformationY value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + TransformationY value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class TransformationYCenterTest : TestBase +{ + [Fact] + public void DoubleValidationWorks() + { + TransformationYCenter value = 0; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + TransformationYCenter value = "string"; + value.Validate(); + } + + [Fact] + public void DoubleSerializationRoundtripWorks() + { + TransformationYCenter value = 0; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + TransformationYCenter value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/VersionInfoTest.cs b/src/Imagekit.Tests/Models/VersionInfoTest.cs new file mode 100644 index 00000000..5b816655 --- /dev/null +++ b/src/Imagekit.Tests/Models/VersionInfoTest.cs @@ -0,0 +1,119 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class VersionInfoTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new VersionInfo { ID = "id", Name = "name" }; + + string expectedID = "id"; + string expectedName = "name"; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedName, model.Name); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new VersionInfo { ID = "id", Name = "name" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new VersionInfo { ID = "id", Name = "name" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedName = "name"; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedName, deserialized.Name); + } + + [Fact] + public void Validation_Works() + { + var model = new VersionInfo { ID = "id", Name = "name" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new VersionInfo { }; + + Assert.Null(model.ID); + Assert.False(model.RawData.ContainsKey("id")); + Assert.Null(model.Name); + Assert.False(model.RawData.ContainsKey("name")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new VersionInfo { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new VersionInfo + { + // Null should be interpreted as omitted for these properties + ID = null, + Name = null, + }; + + Assert.Null(model.ID); + Assert.False(model.RawData.ContainsKey("id")); + Assert.Null(model.Name); + Assert.False(model.RawData.ContainsKey("name")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new VersionInfo + { + // Null should be interpreted as omitted for these properties + ID = null, + Name = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new VersionInfo { ID = "id", Name = "name" }; + + VersionInfo copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/VideoOverlayTest.cs b/src/Imagekit.Tests/Models/VideoOverlayTest.cs new file mode 100644 index 00000000..0219bb68 --- /dev/null +++ b/src/Imagekit.Tests/Models/VideoOverlayTest.cs @@ -0,0 +1,1913 @@ +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models; + +namespace Imagekit.Tests.Models; + +public class VideoOverlayTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new VideoOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = VideoOverlayVideoOverlayEncoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + + ApiEnum expectedLayerMode = LayerMode.Multiply; + OverlayPosition expectedPosition = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }; + OverlayTiming expectedTiming = new() + { + Duration = 0, + End = 0, + Start = 0, + }; + string expectedInput = "input"; + JsonElement expectedType = JsonSerializer.SerializeToElement("video"); + ApiEnum expectedEncoding = + VideoOverlayVideoOverlayEncoding.Auto; + List expectedTransformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ]; + + Assert.Equal(expectedLayerMode, model.LayerMode); + Assert.Equal(expectedPosition, model.Position); + Assert.Equal(expectedTiming, model.Timing); + Assert.Equal(expectedInput, model.Input); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedEncoding, model.Encoding); + Assert.NotNull(model.Transformation); + Assert.Equal(expectedTransformation.Count, model.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], model.Transformation[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new VideoOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = VideoOverlayVideoOverlayEncoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new VideoOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = VideoOverlayVideoOverlayEncoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum expectedLayerMode = LayerMode.Multiply; + OverlayPosition expectedPosition = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }; + OverlayTiming expectedTiming = new() + { + Duration = 0, + End = 0, + Start = 0, + }; + string expectedInput = "input"; + JsonElement expectedType = JsonSerializer.SerializeToElement("video"); + ApiEnum expectedEncoding = + VideoOverlayVideoOverlayEncoding.Auto; + List expectedTransformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ]; + + Assert.Equal(expectedLayerMode, deserialized.LayerMode); + Assert.Equal(expectedPosition, deserialized.Position); + Assert.Equal(expectedTiming, deserialized.Timing); + Assert.Equal(expectedInput, deserialized.Input); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedEncoding, deserialized.Encoding); + Assert.NotNull(deserialized.Transformation); + Assert.Equal(expectedTransformation.Count, deserialized.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], deserialized.Transformation[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new VideoOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = VideoOverlayVideoOverlayEncoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new VideoOverlay { Input = "input" }; + + Assert.Null(model.LayerMode); + Assert.False(model.RawData.ContainsKey("layerMode")); + Assert.Null(model.Position); + Assert.False(model.RawData.ContainsKey("position")); + Assert.Null(model.Timing); + Assert.False(model.RawData.ContainsKey("timing")); + Assert.Null(model.Encoding); + Assert.False(model.RawData.ContainsKey("encoding")); + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new VideoOverlay { Input = "input" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new VideoOverlay + { + Input = "input", + + // Null should be interpreted as omitted for these properties + LayerMode = null, + Position = null, + Timing = null, + Encoding = null, + Transformation = null, + }; + + Assert.Null(model.LayerMode); + Assert.False(model.RawData.ContainsKey("layerMode")); + Assert.Null(model.Position); + Assert.False(model.RawData.ContainsKey("position")); + Assert.Null(model.Timing); + Assert.False(model.RawData.ContainsKey("timing")); + Assert.Null(model.Encoding); + Assert.False(model.RawData.ContainsKey("encoding")); + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new VideoOverlay + { + Input = "input", + + // Null should be interpreted as omitted for these properties + LayerMode = null, + Position = null, + Timing = null, + Encoding = null, + Transformation = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new VideoOverlay + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Input = "input", + Encoding = VideoOverlayVideoOverlayEncoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + + VideoOverlay copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class VideoOverlayVideoOverlayTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new VideoOverlayVideoOverlay + { + Input = "input", + Encoding = VideoOverlayVideoOverlayEncoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + + string expectedInput = "input"; + JsonElement expectedType = JsonSerializer.SerializeToElement("video"); + ApiEnum expectedEncoding = + VideoOverlayVideoOverlayEncoding.Auto; + List expectedTransformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ]; + + Assert.Equal(expectedInput, model.Input); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedEncoding, model.Encoding); + Assert.NotNull(model.Transformation); + Assert.Equal(expectedTransformation.Count, model.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], model.Transformation[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new VideoOverlayVideoOverlay + { + Input = "input", + Encoding = VideoOverlayVideoOverlayEncoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new VideoOverlayVideoOverlay + { + Input = "input", + Encoding = VideoOverlayVideoOverlayEncoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedInput = "input"; + JsonElement expectedType = JsonSerializer.SerializeToElement("video"); + ApiEnum expectedEncoding = + VideoOverlayVideoOverlayEncoding.Auto; + List expectedTransformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ]; + + Assert.Equal(expectedInput, deserialized.Input); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedEncoding, deserialized.Encoding); + Assert.NotNull(deserialized.Transformation); + Assert.Equal(expectedTransformation.Count, deserialized.Transformation.Count); + for (int i = 0; i < expectedTransformation.Count; i++) + { + Assert.Equal(expectedTransformation[i], deserialized.Transformation[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new VideoOverlayVideoOverlay + { + Input = "input", + Encoding = VideoOverlayVideoOverlayEncoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new VideoOverlayVideoOverlay { Input = "input" }; + + Assert.Null(model.Encoding); + Assert.False(model.RawData.ContainsKey("encoding")); + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new VideoOverlayVideoOverlay { Input = "input" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new VideoOverlayVideoOverlay + { + Input = "input", + + // Null should be interpreted as omitted for these properties + Encoding = null, + Transformation = null, + }; + + Assert.Null(model.Encoding); + Assert.False(model.RawData.ContainsKey("encoding")); + Assert.Null(model.Transformation); + Assert.False(model.RawData.ContainsKey("transformation")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new VideoOverlayVideoOverlay + { + Input = "input", + + // Null should be interpreted as omitted for these properties + Encoding = null, + Transformation = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new VideoOverlayVideoOverlay + { + Input = "input", + Encoding = VideoOverlayVideoOverlayEncoding.Auto, + Transformation = + [ + new() + { + AIChangeBackground = "aiChangeBackground", + AIDropShadow = new AIDropShadowDefault(), + AIEdit = "aiEdit", + AIRemoveBackground = AIRemoveBackground.True, + AIRemoveBackgroundExternal = AIRemoveBackgroundExternal.True, + AIRetouch = AIRetouch.True, + AIUpscale = AIUpscale.True, + AIVariation = AIVariation.True, + AspectRatio = "4:3", + AudioCodec = AudioCodec.Aac, + Background = "red", + Blur = 10, + Border = "5_FF0000", + Colorize = "colorize", + ColorProfile = true, + ColorReplace = "colorReplace", + ContrastStretch = ContrastStretch.True, + Crop = Crop.Force, + CropMode = CropMode.PadResize, + DefaultImage = "defaultImage", + Distort = "distort", + Dpr = 2, + Duration = 0, + EndOffset = 0, + Flip = TransformationFlip.H, + Focus = "center", + Format = Format.Auto, + Gradient = new TransformationGradientDefault(), + Grayscale = Grayscale.True, + Height = 200, + Lossless = true, + Metadata = true, + Named = "named", + Opacity = 0, + Original = true, + Overlay = new TextOverlay() + { + LayerMode = LayerMode.Multiply, + Position = new() + { + AnchorPoint = AnchorPoint.Top, + Focus = Focus.Center, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + }, + Timing = new() + { + Duration = 0, + End = 0, + Start = 0, + }, + Text = "text", + Encoding = TextOverlayTextOverlayEncoding.Auto, + Transformation = + [ + new() + { + Alpha = 1, + Background = "background", + Flip = Flip.H, + FontColor = "fontColor", + FontFamily = "fontFamily", + FontSize = 0, + InnerAlignment = InnerAlignment.Left, + LineHeight = 0, + Padding = 0, + Radius = new TextOverlayTransformationRadiusMax(), + Rotation = 0, + Typography = "typography", + Width = 0, + }, + ], + }, + Page = 0, + Progressive = true, + Quality = 80, + Radius = 20, + Raw = "raw", + Rotation = 90, + Shadow = new ShadowDefault(), + Sharpen = new SharpenDefault(), + StartOffset = 0, + StreamingResolutions = [StreamingResolution.V240], + Trim = new TrimDefault(), + UnsharpMask = new UnsharpMaskDefault(), + VideoCodec = VideoCodec.H264, + Width = 300, + X = 0, + XCenter = 0, + Y = 0, + YCenter = 0, + Zoom = 0, + }, + ], + }; + + VideoOverlayVideoOverlay copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class VideoOverlayVideoOverlayEncodingTest : TestBase +{ + [Theory] + [InlineData(VideoOverlayVideoOverlayEncoding.Auto)] + [InlineData(VideoOverlayVideoOverlayEncoding.Plain)] + [InlineData(VideoOverlayVideoOverlayEncoding.Base64)] + public void Validation_Works(VideoOverlayVideoOverlayEncoding rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(VideoOverlayVideoOverlayEncoding.Auto)] + [InlineData(VideoOverlayVideoOverlayEncoding.Plain)] + [InlineData(VideoOverlayVideoOverlayEncoding.Base64)] + public void SerializationRoundtrip_Works(VideoOverlayVideoOverlayEncoding rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/Webhooks/BaseWebhookEventTest.cs b/src/Imagekit.Tests/Models/Webhooks/BaseWebhookEventTest.cs new file mode 100644 index 00000000..ed5b204c --- /dev/null +++ b/src/Imagekit.Tests/Models/Webhooks/BaseWebhookEventTest.cs @@ -0,0 +1,71 @@ +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Webhooks; + +namespace Imagekit.Tests.Models.Webhooks; + +public class BaseWebhookEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new BaseWebhookEvent { ID = "id", Type = "type" }; + + string expectedID = "id"; + string expectedType = "type"; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedType, model.Type); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new BaseWebhookEvent { ID = "id", Type = "type" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new BaseWebhookEvent { ID = "id", Type = "type" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedType = "type"; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedType, deserialized.Type); + } + + [Fact] + public void Validation_Works() + { + var model = new BaseWebhookEvent { ID = "id", Type = "type" }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new BaseWebhookEvent { ID = "id", Type = "type" }; + + BaseWebhookEvent copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Webhooks/FileCreateEventTest.cs b/src/Imagekit.Tests/Models/Webhooks/FileCreateEventTest.cs new file mode 100644 index 00000000..5aa61cf0 --- /dev/null +++ b/src/Imagekit.Tests/Models/Webhooks/FileCreateEventTest.cs @@ -0,0 +1,1155 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Webhooks; +using Files = Imagekit.Models.Files; +using Models = Imagekit.Models; + +namespace Imagekit.Tests.Models.Webhooks; + +public class FileCreateEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new FileCreateEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + Files::File expectedData = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new FileCreateEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new FileCreateEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + Files::File expectedData = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + } + + [Fact] + public void Validation_Works() + { + var model = new FileCreateEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new FileCreateEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + FileCreateEvent copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class FileCreateEventFileCreateEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new FileCreateEventFileCreateEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + Files::File expectedData = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("file.created"); + + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new FileCreateEventFileCreateEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new FileCreateEventFileCreateEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + Files::File expectedData = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("file.created"); + + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + } + + [Fact] + public void Validation_Works() + { + var model = new FileCreateEventFileCreateEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new FileCreateEventFileCreateEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + FileCreateEventFileCreateEvent copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Webhooks/FileDeleteEventTest.cs b/src/Imagekit.Tests/Models/Webhooks/FileDeleteEventTest.cs new file mode 100644 index 00000000..57c3433c --- /dev/null +++ b/src/Imagekit.Tests/Models/Webhooks/FileDeleteEventTest.cs @@ -0,0 +1,256 @@ +using System; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Webhooks; + +namespace Imagekit.Tests.Models.Webhooks; + +public class FileDeleteEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new FileDeleteEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new("fileId"), + }; + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + Data expectedData = new("fileId"); + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new FileDeleteEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new("fileId"), + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new FileDeleteEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new("fileId"), + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + Data expectedData = new("fileId"); + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + } + + [Fact] + public void Validation_Works() + { + var model = new FileDeleteEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new("fileId"), + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new FileDeleteEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new("fileId"), + }; + + FileDeleteEvent copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class FileDeleteEventFileDeleteEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new FileDeleteEventFileDeleteEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new("fileId"), + }; + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + Data expectedData = new("fileId"); + JsonElement expectedType = JsonSerializer.SerializeToElement("file.deleted"); + + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new FileDeleteEventFileDeleteEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new("fileId"), + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new FileDeleteEventFileDeleteEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new("fileId"), + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + Data expectedData = new("fileId"); + JsonElement expectedType = JsonSerializer.SerializeToElement("file.deleted"); + + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + } + + [Fact] + public void Validation_Works() + { + var model = new FileDeleteEventFileDeleteEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new("fileId"), + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new FileDeleteEventFileDeleteEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new("fileId"), + }; + + FileDeleteEventFileDeleteEvent copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class DataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Data { FileID = "fileId" }; + + string expectedFileID = "fileId"; + + Assert.Equal(expectedFileID, model.FileID); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Data { FileID = "fileId" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Data { FileID = "fileId" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + Assert.NotNull(deserialized); + + string expectedFileID = "fileId"; + + Assert.Equal(expectedFileID, deserialized.FileID); + } + + [Fact] + public void Validation_Works() + { + var model = new Data { FileID = "fileId" }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Data { FileID = "fileId" }; + + Data copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Webhooks/FileUpdateEventTest.cs b/src/Imagekit.Tests/Models/Webhooks/FileUpdateEventTest.cs new file mode 100644 index 00000000..fda4dc91 --- /dev/null +++ b/src/Imagekit.Tests/Models/Webhooks/FileUpdateEventTest.cs @@ -0,0 +1,1155 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Webhooks; +using Files = Imagekit.Models.Files; +using Models = Imagekit.Models; + +namespace Imagekit.Tests.Models.Webhooks; + +public class FileUpdateEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new FileUpdateEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + Files::File expectedData = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new FileUpdateEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new FileUpdateEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + Files::File expectedData = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + } + + [Fact] + public void Validation_Works() + { + var model = new FileUpdateEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new FileUpdateEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + FileUpdateEvent copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class FileUpdateEventFileUpdateEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new FileUpdateEventFileUpdateEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + Files::File expectedData = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("file.updated"); + + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new FileUpdateEventFileUpdateEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new FileUpdateEventFileUpdateEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + Files::File expectedData = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("file.updated"); + + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + } + + [Fact] + public void Validation_Works() + { + var model = new FileUpdateEventFileUpdateEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new FileUpdateEventFileUpdateEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + FileUpdateEventFileUpdateEvent copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Webhooks/FileVersionCreateEventTest.cs b/src/Imagekit.Tests/Models/Webhooks/FileVersionCreateEventTest.cs new file mode 100644 index 00000000..bb162333 --- /dev/null +++ b/src/Imagekit.Tests/Models/Webhooks/FileVersionCreateEventTest.cs @@ -0,0 +1,1155 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Webhooks; +using Files = Imagekit.Models.Files; +using Models = Imagekit.Models; + +namespace Imagekit.Tests.Models.Webhooks; + +public class FileVersionCreateEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new FileVersionCreateEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + Files::File expectedData = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new FileVersionCreateEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new FileVersionCreateEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + Files::File expectedData = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + } + + [Fact] + public void Validation_Works() + { + var model = new FileVersionCreateEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new FileVersionCreateEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + FileVersionCreateEvent copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class FileVersionCreateEventFileVersionCreateEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new FileVersionCreateEventFileVersionCreateEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + Files::File expectedData = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("file-version.created"); + + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new FileVersionCreateEventFileVersionCreateEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new FileVersionCreateEventFileVersionCreateEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + Files::File expectedData = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("file-version.created"); + + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + } + + [Fact] + public void Validation_Works() + { + var model = new FileVersionCreateEventFileVersionCreateEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new FileVersionCreateEventFileVersionCreateEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + + FileVersionCreateEventFileVersionCreateEvent copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Webhooks/FileVersionDeleteEventTest.cs b/src/Imagekit.Tests/Models/Webhooks/FileVersionDeleteEventTest.cs new file mode 100644 index 00000000..ad5eb109 --- /dev/null +++ b/src/Imagekit.Tests/Models/Webhooks/FileVersionDeleteEventTest.cs @@ -0,0 +1,304 @@ +using System; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Webhooks; + +namespace Imagekit.Tests.Models.Webhooks; + +public class FileVersionDeleteEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new FileVersionDeleteEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() { FileID = "fileId", VersionID = "versionId" }, + }; + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + FileVersionDeleteEventFileVersionDeleteEventData expectedData = new() + { + FileID = "fileId", + VersionID = "versionId", + }; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new FileVersionDeleteEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() { FileID = "fileId", VersionID = "versionId" }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new FileVersionDeleteEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() { FileID = "fileId", VersionID = "versionId" }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + FileVersionDeleteEventFileVersionDeleteEventData expectedData = new() + { + FileID = "fileId", + VersionID = "versionId", + }; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + } + + [Fact] + public void Validation_Works() + { + var model = new FileVersionDeleteEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() { FileID = "fileId", VersionID = "versionId" }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new FileVersionDeleteEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() { FileID = "fileId", VersionID = "versionId" }, + }; + + FileVersionDeleteEvent copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class FileVersionDeleteEventFileVersionDeleteEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new FileVersionDeleteEventFileVersionDeleteEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() { FileID = "fileId", VersionID = "versionId" }, + }; + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + FileVersionDeleteEventFileVersionDeleteEventData expectedData = new() + { + FileID = "fileId", + VersionID = "versionId", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("file-version.deleted"); + + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new FileVersionDeleteEventFileVersionDeleteEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() { FileID = "fileId", VersionID = "versionId" }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new FileVersionDeleteEventFileVersionDeleteEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() { FileID = "fileId", VersionID = "versionId" }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + FileVersionDeleteEventFileVersionDeleteEventData expectedData = new() + { + FileID = "fileId", + VersionID = "versionId", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("file-version.deleted"); + + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + } + + [Fact] + public void Validation_Works() + { + var model = new FileVersionDeleteEventFileVersionDeleteEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() { FileID = "fileId", VersionID = "versionId" }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new FileVersionDeleteEventFileVersionDeleteEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() { FileID = "fileId", VersionID = "versionId" }, + }; + + FileVersionDeleteEventFileVersionDeleteEvent copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class FileVersionDeleteEventFileVersionDeleteEventDataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new FileVersionDeleteEventFileVersionDeleteEventData + { + FileID = "fileId", + VersionID = "versionId", + }; + + string expectedFileID = "fileId"; + string expectedVersionID = "versionId"; + + Assert.Equal(expectedFileID, model.FileID); + Assert.Equal(expectedVersionID, model.VersionID); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new FileVersionDeleteEventFileVersionDeleteEventData + { + FileID = "fileId", + VersionID = "versionId", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new FileVersionDeleteEventFileVersionDeleteEventData + { + FileID = "fileId", + VersionID = "versionId", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedFileID = "fileId"; + string expectedVersionID = "versionId"; + + Assert.Equal(expectedFileID, deserialized.FileID); + Assert.Equal(expectedVersionID, deserialized.VersionID); + } + + [Fact] + public void Validation_Works() + { + var model = new FileVersionDeleteEventFileVersionDeleteEventData + { + FileID = "fileId", + VersionID = "versionId", + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new FileVersionDeleteEventFileVersionDeleteEventData + { + FileID = "fileId", + VersionID = "versionId", + }; + + FileVersionDeleteEventFileVersionDeleteEventData copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Webhooks/UnsafeUnwrapWebhookEventTest.cs b/src/Imagekit.Tests/Models/Webhooks/UnsafeUnwrapWebhookEventTest.cs new file mode 100644 index 00000000..c97dff30 --- /dev/null +++ b/src/Imagekit.Tests/Models/Webhooks/UnsafeUnwrapWebhookEventTest.cs @@ -0,0 +1,1384 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Files = Imagekit.Models.Files; +using Models = Imagekit.Models; +using Webhooks = Imagekit.Models.Webhooks; + +namespace Imagekit.Tests.Models.Webhooks; + +public class UnsafeUnwrapWebhookEventTest : TestBase +{ + [Fact] + public void VideoTransformationAcceptedValidationWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::VideoTransformationAcceptedEvent() + { + ID = "id", + Type = "video.transformation.accepted", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + Webhooks::VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = Webhooks::AudioCodec.Aac, + AutoRotate = true, + Format = Webhooks::Format.Mp4, + Quality = 0, + StreamProtocol = Webhooks::StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = Webhooks::VideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + value.Validate(); + } + + [Fact] + public void VideoTransformationReadyValidationWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::VideoTransformationReadyEvent() + { + ID = "id", + Type = "video.transformation.ready", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + Timings = new() { DownloadDuration = 0, EncodingDuration = 0 }, + }; + value.Validate(); + } + + [Fact] + public void VideoTransformationErrorValidationWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::VideoTransformationErrorEvent() + { + ID = "id", + Type = "video.transformation.error", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Webhooks::Reason.EncodingFailed), + Options = new() + { + AudioCodec = + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + value.Validate(); + } + + [Fact] + public void UploadPreTransformSuccessValidationWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::UploadPreTransformSuccessEvent() + { + ID = "id", + Type = "upload.pre-transform.success", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = Webhooks::AIAutoDescription.Success, + AITasks = Webhooks::AITasks.Success, + AwsAutoTagging = Webhooks::AwsAutoTagging.Success, + GoogleAutoTagging = Webhooks::GoogleAutoTagging.Success, + RemoveBg = Webhooks::RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + value.Validate(); + } + + [Fact] + public void UploadPreTransformErrorValidationWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::UploadPreTransformErrorEvent() + { + ID = "id", + Type = "upload.pre-transform.error", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Name = "name", + Path = "path", + Transformation = new( + new Webhooks::UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + value.Validate(); + } + + [Fact] + public void UploadPostTransformSuccessValidationWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::UploadPostTransformSuccessEvent() + { + ID = "id", + Type = "upload.post-transform.success", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = + Webhooks::UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + Webhooks::UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + value.Validate(); + } + + [Fact] + public void UploadPostTransformErrorValidationWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::UploadPostTransformErrorEvent() + { + ID = "id", + Type = "upload.post-transform.error", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + value.Validate(); + } + + [Fact] + public void FileCreateValidationWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::FileCreateEvent() + { + ID = "id", + Type = "file.created", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + value.Validate(); + } + + [Fact] + public void FileUpdateValidationWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::FileUpdateEvent() + { + ID = "id", + Type = "file.updated", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + value.Validate(); + } + + [Fact] + public void FileDeleteValidationWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::FileDeleteEvent() + { + ID = "id", + Type = "file.deleted", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new("fileId"), + }; + value.Validate(); + } + + [Fact] + public void FileVersionCreateValidationWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::FileVersionCreateEvent() + { + ID = "id", + Type = "file-version.created", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + value.Validate(); + } + + [Fact] + public void FileVersionDeleteValidationWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::FileVersionDeleteEvent() + { + ID = "id", + Type = "file-version.deleted", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() { FileID = "fileId", VersionID = "versionId" }, + }; + value.Validate(); + } + + [Fact] + public void VideoTransformationAcceptedSerializationRoundtripWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::VideoTransformationAcceptedEvent() + { + ID = "id", + Type = "video.transformation.accepted", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + Webhooks::VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = Webhooks::AudioCodec.Aac, + AutoRotate = true, + Format = Webhooks::Format.Mp4, + Quality = 0, + StreamProtocol = Webhooks::StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = Webhooks::VideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void VideoTransformationReadySerializationRoundtripWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::VideoTransformationReadyEvent() + { + ID = "id", + Type = "video.transformation.ready", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + Timings = new() { DownloadDuration = 0, EncodingDuration = 0 }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void VideoTransformationErrorSerializationRoundtripWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::VideoTransformationErrorEvent() + { + ID = "id", + Type = "video.transformation.error", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Webhooks::Reason.EncodingFailed), + Options = new() + { + AudioCodec = + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void UploadPreTransformSuccessSerializationRoundtripWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::UploadPreTransformSuccessEvent() + { + ID = "id", + Type = "upload.pre-transform.success", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = Webhooks::AIAutoDescription.Success, + AITasks = Webhooks::AITasks.Success, + AwsAutoTagging = Webhooks::AwsAutoTagging.Success, + GoogleAutoTagging = Webhooks::GoogleAutoTagging.Success, + RemoveBg = Webhooks::RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void UploadPreTransformErrorSerializationRoundtripWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::UploadPreTransformErrorEvent() + { + ID = "id", + Type = "upload.pre-transform.error", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Name = "name", + Path = "path", + Transformation = new( + new Webhooks::UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void UploadPostTransformSuccessSerializationRoundtripWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::UploadPostTransformSuccessEvent() + { + ID = "id", + Type = "upload.post-transform.success", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = + Webhooks::UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + Webhooks::UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void UploadPostTransformErrorSerializationRoundtripWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::UploadPostTransformErrorEvent() + { + ID = "id", + Type = "upload.post-transform.error", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void FileCreateSerializationRoundtripWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::FileCreateEvent() + { + ID = "id", + Type = "file.created", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void FileUpdateSerializationRoundtripWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::FileUpdateEvent() + { + ID = "id", + Type = "file.updated", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void FileDeleteSerializationRoundtripWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::FileDeleteEvent() + { + ID = "id", + Type = "file.deleted", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new("fileId"), + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void FileVersionCreateSerializationRoundtripWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::FileVersionCreateEvent() + { + ID = "id", + Type = "file-version.created", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void FileVersionDeleteSerializationRoundtripWorks() + { + Webhooks::UnsafeUnwrapWebhookEvent value = new Webhooks::FileVersionDeleteEvent() + { + ID = "id", + Type = "file-version.deleted", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() { FileID = "fileId", VersionID = "versionId" }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/Webhooks/UnwrapWebhookEventTest.cs b/src/Imagekit.Tests/Models/Webhooks/UnwrapWebhookEventTest.cs new file mode 100644 index 00000000..50b6250f --- /dev/null +++ b/src/Imagekit.Tests/Models/Webhooks/UnwrapWebhookEventTest.cs @@ -0,0 +1,1384 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Files = Imagekit.Models.Files; +using Models = Imagekit.Models; +using Webhooks = Imagekit.Models.Webhooks; + +namespace Imagekit.Tests.Models.Webhooks; + +public class UnwrapWebhookEventTest : TestBase +{ + [Fact] + public void VideoTransformationAcceptedValidationWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::VideoTransformationAcceptedEvent() + { + ID = "id", + Type = "video.transformation.accepted", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + Webhooks::VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = Webhooks::AudioCodec.Aac, + AutoRotate = true, + Format = Webhooks::Format.Mp4, + Quality = 0, + StreamProtocol = Webhooks::StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = Webhooks::VideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + value.Validate(); + } + + [Fact] + public void VideoTransformationReadyValidationWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::VideoTransformationReadyEvent() + { + ID = "id", + Type = "video.transformation.ready", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + Timings = new() { DownloadDuration = 0, EncodingDuration = 0 }, + }; + value.Validate(); + } + + [Fact] + public void VideoTransformationErrorValidationWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::VideoTransformationErrorEvent() + { + ID = "id", + Type = "video.transformation.error", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Webhooks::Reason.EncodingFailed), + Options = new() + { + AudioCodec = + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + value.Validate(); + } + + [Fact] + public void UploadPreTransformSuccessValidationWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::UploadPreTransformSuccessEvent() + { + ID = "id", + Type = "upload.pre-transform.success", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = Webhooks::AIAutoDescription.Success, + AITasks = Webhooks::AITasks.Success, + AwsAutoTagging = Webhooks::AwsAutoTagging.Success, + GoogleAutoTagging = Webhooks::GoogleAutoTagging.Success, + RemoveBg = Webhooks::RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + value.Validate(); + } + + [Fact] + public void UploadPreTransformErrorValidationWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::UploadPreTransformErrorEvent() + { + ID = "id", + Type = "upload.pre-transform.error", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Name = "name", + Path = "path", + Transformation = new( + new Webhooks::UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + value.Validate(); + } + + [Fact] + public void UploadPostTransformSuccessValidationWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::UploadPostTransformSuccessEvent() + { + ID = "id", + Type = "upload.post-transform.success", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = + Webhooks::UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + Webhooks::UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + value.Validate(); + } + + [Fact] + public void UploadPostTransformErrorValidationWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::UploadPostTransformErrorEvent() + { + ID = "id", + Type = "upload.post-transform.error", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + value.Validate(); + } + + [Fact] + public void FileCreateValidationWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::FileCreateEvent() + { + ID = "id", + Type = "file.created", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + value.Validate(); + } + + [Fact] + public void FileUpdateValidationWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::FileUpdateEvent() + { + ID = "id", + Type = "file.updated", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + value.Validate(); + } + + [Fact] + public void FileDeleteValidationWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::FileDeleteEvent() + { + ID = "id", + Type = "file.deleted", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new("fileId"), + }; + value.Validate(); + } + + [Fact] + public void FileVersionCreateValidationWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::FileVersionCreateEvent() + { + ID = "id", + Type = "file-version.created", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + value.Validate(); + } + + [Fact] + public void FileVersionDeleteValidationWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::FileVersionDeleteEvent() + { + ID = "id", + Type = "file-version.deleted", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() { FileID = "fileId", VersionID = "versionId" }, + }; + value.Validate(); + } + + [Fact] + public void VideoTransformationAcceptedSerializationRoundtripWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::VideoTransformationAcceptedEvent() + { + ID = "id", + Type = "video.transformation.accepted", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + Webhooks::VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = Webhooks::AudioCodec.Aac, + AutoRotate = true, + Format = Webhooks::Format.Mp4, + Quality = 0, + StreamProtocol = Webhooks::StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = Webhooks::VideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void VideoTransformationReadySerializationRoundtripWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::VideoTransformationReadyEvent() + { + ID = "id", + Type = "video.transformation.ready", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + Timings = new() { DownloadDuration = 0, EncodingDuration = 0 }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void VideoTransformationErrorSerializationRoundtripWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::VideoTransformationErrorEvent() + { + ID = "id", + Type = "video.transformation.error", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Webhooks::Reason.EncodingFailed), + Options = new() + { + AudioCodec = + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void UploadPreTransformSuccessSerializationRoundtripWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::UploadPreTransformSuccessEvent() + { + ID = "id", + Type = "upload.pre-transform.success", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = Webhooks::AIAutoDescription.Success, + AITasks = Webhooks::AITasks.Success, + AwsAutoTagging = Webhooks::AwsAutoTagging.Success, + GoogleAutoTagging = Webhooks::GoogleAutoTagging.Success, + RemoveBg = Webhooks::RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void UploadPreTransformErrorSerializationRoundtripWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::UploadPreTransformErrorEvent() + { + ID = "id", + Type = "upload.pre-transform.error", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Name = "name", + Path = "path", + Transformation = new( + new Webhooks::UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void UploadPostTransformSuccessSerializationRoundtripWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::UploadPostTransformSuccessEvent() + { + ID = "id", + Type = "upload.post-transform.success", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = + Webhooks::UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + Webhooks::UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void UploadPostTransformErrorSerializationRoundtripWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::UploadPostTransformErrorEvent() + { + ID = "id", + Type = "upload.post-transform.error", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void FileCreateSerializationRoundtripWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::FileCreateEvent() + { + ID = "id", + Type = "file.created", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void FileUpdateSerializationRoundtripWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::FileUpdateEvent() + { + ID = "id", + Type = "file.updated", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void FileDeleteSerializationRoundtripWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::FileDeleteEvent() + { + ID = "id", + Type = "file.deleted", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new("fileId"), + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void FileVersionCreateSerializationRoundtripWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::FileVersionCreateEvent() + { + ID = "id", + Type = "file-version.created", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + HasAlpha = true, + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Mime = "mime", + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + Thumbnail = "https://example.com", + Type = Files::Type.File, + UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Url = "https://example.com", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void FileVersionDeleteSerializationRoundtripWorks() + { + Webhooks::UnwrapWebhookEvent value = new Webhooks::FileVersionDeleteEvent() + { + ID = "id", + Type = "file-version.deleted", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() { FileID = "fileId", VersionID = "versionId" }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/Webhooks/UploadPostTransformErrorEventTest.cs b/src/Imagekit.Tests/Models/Webhooks/UploadPostTransformErrorEventTest.cs new file mode 100644 index 00000000..bc318e7f --- /dev/null +++ b/src/Imagekit.Tests/Models/Webhooks/UploadPostTransformErrorEventTest.cs @@ -0,0 +1,1082 @@ +using System; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Webhooks = Imagekit.Models.Webhooks; + +namespace Imagekit.Tests.Models.Webhooks; + +public class UploadPostTransformErrorEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Webhooks::UploadPostTransformErrorEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + Webhooks::UploadPostTransformErrorEventUploadPostTransformErrorEventData expectedData = + new() + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }; + Webhooks::Request expectedRequest = new() + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + Assert.Equal(expectedRequest, model.Request); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Webhooks::UploadPostTransformErrorEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Webhooks::UploadPostTransformErrorEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + Webhooks::UploadPostTransformErrorEventUploadPostTransformErrorEventData expectedData = + new() + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }; + Webhooks::Request expectedRequest = new() + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + Assert.Equal(expectedRequest, deserialized.Request); + } + + [Fact] + public void Validation_Works() + { + var model = new Webhooks::UploadPostTransformErrorEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Webhooks::UploadPostTransformErrorEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + + Webhooks::UploadPostTransformErrorEvent copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class UploadPostTransformErrorEventUploadPostTransformErrorEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Webhooks::UploadPostTransformErrorEventUploadPostTransformErrorEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + Webhooks::UploadPostTransformErrorEventUploadPostTransformErrorEventData expectedData = + new() + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }; + Webhooks::Request expectedRequest = new() + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("upload.post-transform.error"); + + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + Assert.Equal(expectedRequest, model.Request); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Webhooks::UploadPostTransformErrorEventUploadPostTransformErrorEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Webhooks::UploadPostTransformErrorEventUploadPostTransformErrorEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + Webhooks::UploadPostTransformErrorEventUploadPostTransformErrorEventData expectedData = + new() + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }; + Webhooks::Request expectedRequest = new() + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("upload.post-transform.error"); + + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + Assert.Equal(expectedRequest, deserialized.Request); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + } + + [Fact] + public void Validation_Works() + { + var model = new Webhooks::UploadPostTransformErrorEventUploadPostTransformErrorEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Webhooks::UploadPostTransformErrorEventUploadPostTransformErrorEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + + Webhooks::UploadPostTransformErrorEventUploadPostTransformErrorEvent copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class UploadPostTransformErrorEventUploadPostTransformErrorEventDataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Webhooks::UploadPostTransformErrorEventUploadPostTransformErrorEventData + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }; + + string expectedFileID = "fileId"; + string expectedName = "name"; + string expectedPath = "path"; + Webhooks::Transformation expectedTransformation = new( + new Webhooks::Error("encoding_failed") + ); + string expectedUrl = "https://example.com"; + + Assert.Equal(expectedFileID, model.FileID); + Assert.Equal(expectedName, model.Name); + Assert.Equal(expectedPath, model.Path); + Assert.Equal(expectedTransformation, model.Transformation); + Assert.Equal(expectedUrl, model.Url); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Webhooks::UploadPostTransformErrorEventUploadPostTransformErrorEventData + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Webhooks::UploadPostTransformErrorEventUploadPostTransformErrorEventData + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedFileID = "fileId"; + string expectedName = "name"; + string expectedPath = "path"; + Webhooks::Transformation expectedTransformation = new( + new Webhooks::Error("encoding_failed") + ); + string expectedUrl = "https://example.com"; + + Assert.Equal(expectedFileID, deserialized.FileID); + Assert.Equal(expectedName, deserialized.Name); + Assert.Equal(expectedPath, deserialized.Path); + Assert.Equal(expectedTransformation, deserialized.Transformation); + Assert.Equal(expectedUrl, deserialized.Url); + } + + [Fact] + public void Validation_Works() + { + var model = new Webhooks::UploadPostTransformErrorEventUploadPostTransformErrorEventData + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Webhooks::UploadPostTransformErrorEventUploadPostTransformErrorEventData + { + FileID = "fileId", + Name = "name", + Path = "path", + Transformation = new(new Webhooks::Error("encoding_failed")), + Url = "https://example.com", + }; + + Webhooks::UploadPostTransformErrorEventUploadPostTransformErrorEventData copied = new( + model + ); + + Assert.Equal(model, copied); + } +} + +public class TransformationTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Webhooks::Transformation { Error = new("encoding_failed") }; + + Webhooks::Error expectedError = new("encoding_failed"); + + Assert.Equal(expectedError, model.Error); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Webhooks::Transformation { Error = new("encoding_failed") }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Webhooks::Transformation { Error = new("encoding_failed") }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + Webhooks::Error expectedError = new("encoding_failed"); + + Assert.Equal(expectedError, deserialized.Error); + } + + [Fact] + public void Validation_Works() + { + var model = new Webhooks::Transformation { Error = new("encoding_failed") }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Webhooks::Transformation { Error = new("encoding_failed") }; + + Webhooks::Transformation copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ErrorTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Webhooks::Error { Reason = "encoding_failed" }; + + string expectedReason = "encoding_failed"; + + Assert.Equal(expectedReason, model.Reason); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Webhooks::Error { Reason = "encoding_failed" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Webhooks::Error { Reason = "encoding_failed" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedReason = "encoding_failed"; + + Assert.Equal(expectedReason, deserialized.Reason); + } + + [Fact] + public void Validation_Works() + { + var model = new Webhooks::Error { Reason = "encoding_failed" }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Webhooks::Error { Reason = "encoding_failed" }; + + Webhooks::Error copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class RequestTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Webhooks::Request + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }; + + Webhooks::RequestTransformation expectedTransformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }; + string expectedXRequestID = "x_request_id"; + + Assert.Equal(expectedTransformation, model.Transformation); + Assert.Equal(expectedXRequestID, model.XRequestID); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Webhooks::Request + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Webhooks::Request + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + Webhooks::RequestTransformation expectedTransformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }; + string expectedXRequestID = "x_request_id"; + + Assert.Equal(expectedTransformation, deserialized.Transformation); + Assert.Equal(expectedXRequestID, deserialized.XRequestID); + } + + [Fact] + public void Validation_Works() + { + var model = new Webhooks::Request + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Webhooks::Request + { + Transformation = new() + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }; + + Webhooks::Request copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class RequestTransformationTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Webhooks::RequestTransformation + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }; + + ApiEnum expectedType = Webhooks::Type.Transformation; + ApiEnum expectedProtocol = Webhooks::Protocol.Hls; + string expectedValue = "value"; + + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedProtocol, model.Protocol); + Assert.Equal(expectedValue, model.Value); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Webhooks::RequestTransformation + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Webhooks::RequestTransformation + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum expectedType = Webhooks::Type.Transformation; + ApiEnum expectedProtocol = Webhooks::Protocol.Hls; + string expectedValue = "value"; + + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedProtocol, deserialized.Protocol); + Assert.Equal(expectedValue, deserialized.Value); + } + + [Fact] + public void Validation_Works() + { + var model = new Webhooks::RequestTransformation + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Webhooks::RequestTransformation { Type = Webhooks::Type.Transformation }; + + Assert.Null(model.Protocol); + Assert.False(model.RawData.ContainsKey("protocol")); + Assert.Null(model.Value); + Assert.False(model.RawData.ContainsKey("value")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Webhooks::RequestTransformation { Type = Webhooks::Type.Transformation }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Webhooks::RequestTransformation + { + Type = Webhooks::Type.Transformation, + + // Null should be interpreted as omitted for these properties + Protocol = null, + Value = null, + }; + + Assert.Null(model.Protocol); + Assert.False(model.RawData.ContainsKey("protocol")); + Assert.Null(model.Value); + Assert.False(model.RawData.ContainsKey("value")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Webhooks::RequestTransformation + { + Type = Webhooks::Type.Transformation, + + // Null should be interpreted as omitted for these properties + Protocol = null, + Value = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Webhooks::RequestTransformation + { + Type = Webhooks::Type.Transformation, + Protocol = Webhooks::Protocol.Hls, + Value = "value", + }; + + Webhooks::RequestTransformation copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class TypeTest : TestBase +{ + [Theory] + [InlineData(Webhooks::Type.Transformation)] + [InlineData(Webhooks::Type.Abs)] + [InlineData(Webhooks::Type.GifToVideo)] + [InlineData(Webhooks::Type.Thumbnail)] + public void Validation_Works(Webhooks::Type rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Webhooks::Type.Transformation)] + [InlineData(Webhooks::Type.Abs)] + [InlineData(Webhooks::Type.GifToVideo)] + [InlineData(Webhooks::Type.Thumbnail)] + public void SerializationRoundtrip_Works(Webhooks::Type rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class ProtocolTest : TestBase +{ + [Theory] + [InlineData(Webhooks::Protocol.Hls)] + [InlineData(Webhooks::Protocol.Dash)] + public void Validation_Works(Webhooks::Protocol rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Webhooks::Protocol.Hls)] + [InlineData(Webhooks::Protocol.Dash)] + public void SerializationRoundtrip_Works(Webhooks::Protocol rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/Webhooks/UploadPostTransformSuccessEventTest.cs b/src/Imagekit.Tests/Models/Webhooks/UploadPostTransformSuccessEventTest.cs new file mode 100644 index 00000000..f7f2b644 --- /dev/null +++ b/src/Imagekit.Tests/Models/Webhooks/UploadPostTransformSuccessEventTest.cs @@ -0,0 +1,1067 @@ +using System; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Webhooks; + +namespace Imagekit.Tests.Models.Webhooks; + +public class UploadPostTransformSuccessEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new UploadPostTransformSuccessEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + UploadPostTransformSuccessEventUploadPostTransformSuccessEventData expectedData = new() + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }; + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest expectedRequest = + new() + { + Transformation = new() + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + Assert.Equal(expectedRequest, model.Request); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new UploadPostTransformSuccessEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new UploadPostTransformSuccessEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + UploadPostTransformSuccessEventUploadPostTransformSuccessEventData expectedData = new() + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }; + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest expectedRequest = + new() + { + Transformation = new() + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + Assert.Equal(expectedRequest, deserialized.Request); + } + + [Fact] + public void Validation_Works() + { + var model = new UploadPostTransformSuccessEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new UploadPostTransformSuccessEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + + UploadPostTransformSuccessEvent copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class UploadPostTransformSuccessEventUploadPostTransformSuccessEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new UploadPostTransformSuccessEventUploadPostTransformSuccessEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + UploadPostTransformSuccessEventUploadPostTransformSuccessEventData expectedData = new() + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }; + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest expectedRequest = + new() + { + Transformation = new() + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement( + "upload.post-transform.success" + ); + + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + Assert.Equal(expectedRequest, model.Request); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new UploadPostTransformSuccessEventUploadPostTransformSuccessEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new UploadPostTransformSuccessEventUploadPostTransformSuccessEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + UploadPostTransformSuccessEventUploadPostTransformSuccessEventData expectedData = new() + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }; + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest expectedRequest = + new() + { + Transformation = new() + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement( + "upload.post-transform.success" + ); + + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + Assert.Equal(expectedRequest, deserialized.Request); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + } + + [Fact] + public void Validation_Works() + { + var model = new UploadPostTransformSuccessEventUploadPostTransformSuccessEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new UploadPostTransformSuccessEventUploadPostTransformSuccessEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }, + Request = new() + { + Transformation = new() + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }, + }; + + UploadPostTransformSuccessEventUploadPostTransformSuccessEvent copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class UploadPostTransformSuccessEventUploadPostTransformSuccessEventDataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new UploadPostTransformSuccessEventUploadPostTransformSuccessEventData + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }; + + string expectedFileID = "fileId"; + string expectedName = "name"; + string expectedUrl = "https://example.com"; + + Assert.Equal(expectedFileID, model.FileID); + Assert.Equal(expectedName, model.Name); + Assert.Equal(expectedUrl, model.Url); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new UploadPostTransformSuccessEventUploadPostTransformSuccessEventData + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new UploadPostTransformSuccessEventUploadPostTransformSuccessEventData + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedFileID = "fileId"; + string expectedName = "name"; + string expectedUrl = "https://example.com"; + + Assert.Equal(expectedFileID, deserialized.FileID); + Assert.Equal(expectedName, deserialized.Name); + Assert.Equal(expectedUrl, deserialized.Url); + } + + [Fact] + public void Validation_Works() + { + var model = new UploadPostTransformSuccessEventUploadPostTransformSuccessEventData + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new UploadPostTransformSuccessEventUploadPostTransformSuccessEventData + { + FileID = "fileId", + Name = "name", + Url = "https://example.com", + }; + + UploadPostTransformSuccessEventUploadPostTransformSuccessEventData copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest + { + Transformation = new() + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }; + + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation expectedTransformation = + new() + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }; + string expectedXRequestID = "x_request_id"; + + Assert.Equal(expectedTransformation, model.Transformation); + Assert.Equal(expectedXRequestID, model.XRequestID); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest + { + Transformation = new() + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest + { + Transformation = new() + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation expectedTransformation = + new() + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }; + string expectedXRequestID = "x_request_id"; + + Assert.Equal(expectedTransformation, deserialized.Transformation); + Assert.Equal(expectedXRequestID, deserialized.XRequestID); + } + + [Fact] + public void Validation_Works() + { + var model = new UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest + { + Transformation = new() + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest + { + Transformation = new() + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }, + XRequestID = "x_request_id", + }; + + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationTest + : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = + new UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }; + + ApiEnum< + string, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType + > expectedType = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation; + ApiEnum< + string, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol + > expectedProtocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls; + string expectedValue = "value"; + + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedProtocol, model.Protocol); + Assert.Equal(expectedValue, model.Value); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = + new UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = + new UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum< + string, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType + > expectedType = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation; + ApiEnum< + string, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol + > expectedProtocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls; + string expectedValue = "value"; + + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedProtocol, deserialized.Protocol); + Assert.Equal(expectedValue, deserialized.Value); + } + + [Fact] + public void Validation_Works() + { + var model = + new UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = + new UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + }; + + Assert.Null(model.Protocol); + Assert.False(model.RawData.ContainsKey("protocol")); + Assert.Null(model.Value); + Assert.False(model.RawData.ContainsKey("value")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = + new UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = + new UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + + // Null should be interpreted as omitted for these properties + Protocol = null, + Value = null, + }; + + Assert.Null(model.Protocol); + Assert.False(model.RawData.ContainsKey("protocol")); + Assert.Null(model.Value); + Assert.False(model.RawData.ContainsKey("value")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = + new UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + + // Null should be interpreted as omitted for these properties + Protocol = null, + Value = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = + new UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation + { + Type = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + Protocol = + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + Value = "value", + }; + + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation copied = + new(model); + + Assert.Equal(model, copied); + } +} + +public class UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationTypeTest + : TestBase +{ + [Theory] + [InlineData( + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation + )] + [InlineData( + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Abs + )] + [InlineData( + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.GifToVideo + )] + [InlineData( + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Thumbnail + )] + public void Validation_Works( + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType + > value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData( + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation + )] + [InlineData( + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Abs + )] + [InlineData( + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.GifToVideo + )] + [InlineData( + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Thumbnail + )] + public void SerializationRoundtrip_Works( + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType + > value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocolTest + : TestBase +{ + [Theory] + [InlineData( + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls + )] + [InlineData( + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Dash + )] + public void Validation_Works( + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol + > value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData( + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls + )] + [InlineData( + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Dash + )] + public void SerializationRoundtrip_Works( + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol + > value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Imagekit.Tests/Models/Webhooks/UploadPreTransformErrorEventTest.cs b/src/Imagekit.Tests/Models/Webhooks/UploadPreTransformErrorEventTest.cs new file mode 100644 index 00000000..ff28093e --- /dev/null +++ b/src/Imagekit.Tests/Models/Webhooks/UploadPreTransformErrorEventTest.cs @@ -0,0 +1,747 @@ +using System; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Models.Webhooks; + +namespace Imagekit.Tests.Models.Webhooks; + +public class UploadPreTransformErrorEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new UploadPreTransformErrorEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Name = "name", + Path = "path", + Transformation = new( + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + UploadPreTransformErrorEventUploadPreTransformErrorEventData expectedData = new() + { + Name = "name", + Path = "path", + Transformation = new( + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }; + UploadPreTransformErrorEventUploadPreTransformErrorEventRequest expectedRequest = new() + { + Transformation = "transformation", + XRequestID = "x_request_id", + }; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + Assert.Equal(expectedRequest, model.Request); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new UploadPreTransformErrorEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Name = "name", + Path = "path", + Transformation = new( + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new UploadPreTransformErrorEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Name = "name", + Path = "path", + Transformation = new( + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + UploadPreTransformErrorEventUploadPreTransformErrorEventData expectedData = new() + { + Name = "name", + Path = "path", + Transformation = new( + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }; + UploadPreTransformErrorEventUploadPreTransformErrorEventRequest expectedRequest = new() + { + Transformation = "transformation", + XRequestID = "x_request_id", + }; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + Assert.Equal(expectedRequest, deserialized.Request); + } + + [Fact] + public void Validation_Works() + { + var model = new UploadPreTransformErrorEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Name = "name", + Path = "path", + Transformation = new( + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new UploadPreTransformErrorEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Name = "name", + Path = "path", + Transformation = new( + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + + UploadPreTransformErrorEvent copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class UploadPreTransformErrorEventUploadPreTransformErrorEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new UploadPreTransformErrorEventUploadPreTransformErrorEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Name = "name", + Path = "path", + Transformation = new( + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + UploadPreTransformErrorEventUploadPreTransformErrorEventData expectedData = new() + { + Name = "name", + Path = "path", + Transformation = new( + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }; + UploadPreTransformErrorEventUploadPreTransformErrorEventRequest expectedRequest = new() + { + Transformation = "transformation", + XRequestID = "x_request_id", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("upload.pre-transform.error"); + + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + Assert.Equal(expectedRequest, model.Request); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new UploadPreTransformErrorEventUploadPreTransformErrorEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Name = "name", + Path = "path", + Transformation = new( + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new UploadPreTransformErrorEventUploadPreTransformErrorEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Name = "name", + Path = "path", + Transformation = new( + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + UploadPreTransformErrorEventUploadPreTransformErrorEventData expectedData = new() + { + Name = "name", + Path = "path", + Transformation = new( + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }; + UploadPreTransformErrorEventUploadPreTransformErrorEventRequest expectedRequest = new() + { + Transformation = "transformation", + XRequestID = "x_request_id", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("upload.pre-transform.error"); + + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + Assert.Equal(expectedRequest, deserialized.Request); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + } + + [Fact] + public void Validation_Works() + { + var model = new UploadPreTransformErrorEventUploadPreTransformErrorEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Name = "name", + Path = "path", + Transformation = new( + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new UploadPreTransformErrorEventUploadPreTransformErrorEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Name = "name", + Path = "path", + Transformation = new( + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + + UploadPreTransformErrorEventUploadPreTransformErrorEvent copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class UploadPreTransformErrorEventUploadPreTransformErrorEventDataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new UploadPreTransformErrorEventUploadPreTransformErrorEventData + { + Name = "name", + Path = "path", + Transformation = new( + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }; + + string expectedName = "name"; + string expectedPath = "path"; + UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformation expectedTransformation = + new( + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ); + + Assert.Equal(expectedName, model.Name); + Assert.Equal(expectedPath, model.Path); + Assert.Equal(expectedTransformation, model.Transformation); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new UploadPreTransformErrorEventUploadPreTransformErrorEventData + { + Name = "name", + Path = "path", + Transformation = new( + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new UploadPreTransformErrorEventUploadPreTransformErrorEventData + { + Name = "name", + Path = "path", + Transformation = new( + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedName = "name"; + string expectedPath = "path"; + UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformation expectedTransformation = + new( + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ); + + Assert.Equal(expectedName, deserialized.Name); + Assert.Equal(expectedPath, deserialized.Path); + Assert.Equal(expectedTransformation, deserialized.Transformation); + } + + [Fact] + public void Validation_Works() + { + var model = new UploadPreTransformErrorEventUploadPreTransformErrorEventData + { + Name = "name", + Path = "path", + Transformation = new( + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new UploadPreTransformErrorEventUploadPreTransformErrorEventData + { + Name = "name", + Path = "path", + Transformation = new( + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + "encoding_failed" + ) + ), + }; + + UploadPreTransformErrorEventUploadPreTransformErrorEventData copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationTest + : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformation + { + Error = new("encoding_failed"), + }; + + UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError expectedError = + new("encoding_failed"); + + Assert.Equal(expectedError, model.Error); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformation + { + Error = new("encoding_failed"), + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformation + { + Error = new("encoding_failed"), + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError expectedError = + new("encoding_failed"); + + Assert.Equal(expectedError, deserialized.Error); + } + + [Fact] + public void Validation_Works() + { + var model = new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformation + { + Error = new("encoding_failed"), + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformation + { + Error = new("encoding_failed"), + }; + + UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformation copied = new( + model + ); + + Assert.Equal(model, copied); + } +} + +public class UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationErrorTest + : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError + { + Reason = "encoding_failed", + }; + + string expectedReason = "encoding_failed"; + + Assert.Equal(expectedReason, model.Reason); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError + { + Reason = "encoding_failed", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError + { + Reason = "encoding_failed", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedReason = "encoding_failed"; + + Assert.Equal(expectedReason, deserialized.Reason); + } + + [Fact] + public void Validation_Works() + { + var model = + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError + { + Reason = "encoding_failed", + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = + new UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError + { + Reason = "encoding_failed", + }; + + UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError copied = + new(model); + + Assert.Equal(model, copied); + } +} + +public class UploadPreTransformErrorEventUploadPreTransformErrorEventRequestTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new UploadPreTransformErrorEventUploadPreTransformErrorEventRequest + { + Transformation = "transformation", + XRequestID = "x_request_id", + }; + + string expectedTransformation = "transformation"; + string expectedXRequestID = "x_request_id"; + + Assert.Equal(expectedTransformation, model.Transformation); + Assert.Equal(expectedXRequestID, model.XRequestID); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new UploadPreTransformErrorEventUploadPreTransformErrorEventRequest + { + Transformation = "transformation", + XRequestID = "x_request_id", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new UploadPreTransformErrorEventUploadPreTransformErrorEventRequest + { + Transformation = "transformation", + XRequestID = "x_request_id", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedTransformation = "transformation"; + string expectedXRequestID = "x_request_id"; + + Assert.Equal(expectedTransformation, deserialized.Transformation); + Assert.Equal(expectedXRequestID, deserialized.XRequestID); + } + + [Fact] + public void Validation_Works() + { + var model = new UploadPreTransformErrorEventUploadPreTransformErrorEventRequest + { + Transformation = "transformation", + XRequestID = "x_request_id", + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new UploadPreTransformErrorEventUploadPreTransformErrorEventRequest + { + Transformation = "transformation", + XRequestID = "x_request_id", + }; + + UploadPreTransformErrorEventUploadPreTransformErrorEventRequest copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Webhooks/UploadPreTransformSuccessEventTest.cs b/src/Imagekit.Tests/Models/Webhooks/UploadPreTransformSuccessEventTest.cs new file mode 100644 index 00000000..aeea4c74 --- /dev/null +++ b/src/Imagekit.Tests/Models/Webhooks/UploadPreTransformSuccessEventTest.cs @@ -0,0 +1,5003 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Webhooks; +using Files = Imagekit.Models.Files; +using Models = Imagekit.Models; + +namespace Imagekit.Tests.Models.Webhooks; + +public class UploadPreTransformSuccessEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new UploadPreTransformSuccessEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + UploadPreTransformSuccessEventUploadPreTransformSuccessEventData expectedData = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest expectedRequest = new() + { + Transformation = "transformation", + XRequestID = "x_request_id", + }; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + Assert.Equal(expectedRequest, model.Request); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new UploadPreTransformSuccessEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new UploadPreTransformSuccessEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + UploadPreTransformSuccessEventUploadPreTransformSuccessEventData expectedData = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest expectedRequest = new() + { + Transformation = "transformation", + XRequestID = "x_request_id", + }; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + Assert.Equal(expectedRequest, deserialized.Request); + } + + [Fact] + public void Validation_Works() + { + var model = new UploadPreTransformSuccessEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new UploadPreTransformSuccessEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + + UploadPreTransformSuccessEvent copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class UploadPreTransformSuccessEventUploadPreTransformSuccessEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + UploadPreTransformSuccessEventUploadPreTransformSuccessEventData expectedData = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest expectedRequest = new() + { + Transformation = "transformation", + XRequestID = "x_request_id", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement( + "upload.pre-transform.success" + ); + + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + Assert.Equal(expectedRequest, model.Request); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + UploadPreTransformSuccessEventUploadPreTransformSuccessEventData expectedData = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest expectedRequest = new() + { + Transformation = "transformation", + XRequestID = "x_request_id", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement( + "upload.pre-transform.success" + ); + + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + Assert.Equal(expectedRequest, deserialized.Request); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + } + + [Fact] + public void Validation_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }, + Request = new() { Transformation = "transformation", XRequestID = "x_request_id" }, + }; + + UploadPreTransformSuccessEventUploadPreTransformSuccessEvent copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class UploadPreTransformSuccessEventUploadPreTransformSuccessEventDataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEventData + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + List expectedAITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ]; + string expectedAudioCodec = "audioCodec"; + long expectedBitRate = 0; + string expectedCustomCoordinates = "customCoordinates"; + Dictionary expectedCustomMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + string expectedDescription = "description"; + long expectedDuration = 0; + Dictionary expectedEmbeddedMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + ExtensionStatus expectedExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }; + string expectedFileID = "fileId"; + string expectedFilePath = "filePath"; + string expectedFileType = "fileType"; + double expectedHeight = 0; + bool expectedIsPrivateFile = true; + bool expectedIsPublished = true; + Files::FileMetadata expectedMetadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }; + string expectedName = "name"; + Dictionary expectedSelectedFieldsSchema = new() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }; + double expectedSize = 0; + List expectedTags = ["string"]; + string expectedThumbnailUrl = "thumbnailUrl"; + string expectedUrl = "url"; + Models::VersionInfo expectedVersionInfo = new() { ID = "id", Name = "name" }; + string expectedVideoCodec = "videoCodec"; + double expectedWidth = 0; + + Assert.NotNull(model.AITags); + Assert.Equal(expectedAITags.Count, model.AITags.Count); + for (int i = 0; i < expectedAITags.Count; i++) + { + Assert.Equal(expectedAITags[i], model.AITags[i]); + } + Assert.Equal(expectedAudioCodec, model.AudioCodec); + Assert.Equal(expectedBitRate, model.BitRate); + Assert.Equal(expectedCustomCoordinates, model.CustomCoordinates); + Assert.NotNull(model.CustomMetadata); + Assert.Equal(expectedCustomMetadata.Count, model.CustomMetadata.Count); + foreach (var item in expectedCustomMetadata) + { + Assert.True(model.CustomMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, model.CustomMetadata[item.Key])); + } + Assert.Equal(expectedDescription, model.Description); + Assert.Equal(expectedDuration, model.Duration); + Assert.NotNull(model.EmbeddedMetadata); + Assert.Equal(expectedEmbeddedMetadata.Count, model.EmbeddedMetadata.Count); + foreach (var item in expectedEmbeddedMetadata) + { + Assert.True(model.EmbeddedMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, model.EmbeddedMetadata[item.Key])); + } + Assert.Equal(expectedExtensionStatus, model.ExtensionStatus); + Assert.Equal(expectedFileID, model.FileID); + Assert.Equal(expectedFilePath, model.FilePath); + Assert.Equal(expectedFileType, model.FileType); + Assert.Equal(expectedHeight, model.Height); + Assert.Equal(expectedIsPrivateFile, model.IsPrivateFile); + Assert.Equal(expectedIsPublished, model.IsPublished); + Assert.Equal(expectedMetadata, model.Metadata); + Assert.Equal(expectedName, model.Name); + Assert.NotNull(model.SelectedFieldsSchema); + Assert.Equal(expectedSelectedFieldsSchema.Count, model.SelectedFieldsSchema.Count); + foreach (var item in expectedSelectedFieldsSchema) + { + Assert.True(model.SelectedFieldsSchema.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.SelectedFieldsSchema[item.Key]); + } + Assert.Equal(expectedSize, model.Size); + Assert.NotNull(model.Tags); + Assert.Equal(expectedTags.Count, model.Tags.Count); + for (int i = 0; i < expectedTags.Count; i++) + { + Assert.Equal(expectedTags[i], model.Tags[i]); + } + Assert.Equal(expectedThumbnailUrl, model.ThumbnailUrl); + Assert.Equal(expectedUrl, model.Url); + Assert.Equal(expectedVersionInfo, model.VersionInfo); + Assert.Equal(expectedVideoCodec, model.VideoCodec); + Assert.Equal(expectedWidth, model.Width); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEventData + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEventData + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + List expectedAITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ]; + string expectedAudioCodec = "audioCodec"; + long expectedBitRate = 0; + string expectedCustomCoordinates = "customCoordinates"; + Dictionary expectedCustomMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + string expectedDescription = "description"; + long expectedDuration = 0; + Dictionary expectedEmbeddedMetadata = new() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }; + ExtensionStatus expectedExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }; + string expectedFileID = "fileId"; + string expectedFilePath = "filePath"; + string expectedFileType = "fileType"; + double expectedHeight = 0; + bool expectedIsPrivateFile = true; + bool expectedIsPublished = true; + Files::FileMetadata expectedMetadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }; + string expectedName = "name"; + Dictionary expectedSelectedFieldsSchema = new() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }; + double expectedSize = 0; + List expectedTags = ["string"]; + string expectedThumbnailUrl = "thumbnailUrl"; + string expectedUrl = "url"; + Models::VersionInfo expectedVersionInfo = new() { ID = "id", Name = "name" }; + string expectedVideoCodec = "videoCodec"; + double expectedWidth = 0; + + Assert.NotNull(deserialized.AITags); + Assert.Equal(expectedAITags.Count, deserialized.AITags.Count); + for (int i = 0; i < expectedAITags.Count; i++) + { + Assert.Equal(expectedAITags[i], deserialized.AITags[i]); + } + Assert.Equal(expectedAudioCodec, deserialized.AudioCodec); + Assert.Equal(expectedBitRate, deserialized.BitRate); + Assert.Equal(expectedCustomCoordinates, deserialized.CustomCoordinates); + Assert.NotNull(deserialized.CustomMetadata); + Assert.Equal(expectedCustomMetadata.Count, deserialized.CustomMetadata.Count); + foreach (var item in expectedCustomMetadata) + { + Assert.True(deserialized.CustomMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, deserialized.CustomMetadata[item.Key])); + } + Assert.Equal(expectedDescription, deserialized.Description); + Assert.Equal(expectedDuration, deserialized.Duration); + Assert.NotNull(deserialized.EmbeddedMetadata); + Assert.Equal(expectedEmbeddedMetadata.Count, deserialized.EmbeddedMetadata.Count); + foreach (var item in expectedEmbeddedMetadata) + { + Assert.True(deserialized.EmbeddedMetadata.TryGetValue(item.Key, out var value)); + + Assert.True(JsonElement.DeepEquals(value, deserialized.EmbeddedMetadata[item.Key])); + } + Assert.Equal(expectedExtensionStatus, deserialized.ExtensionStatus); + Assert.Equal(expectedFileID, deserialized.FileID); + Assert.Equal(expectedFilePath, deserialized.FilePath); + Assert.Equal(expectedFileType, deserialized.FileType); + Assert.Equal(expectedHeight, deserialized.Height); + Assert.Equal(expectedIsPrivateFile, deserialized.IsPrivateFile); + Assert.Equal(expectedIsPublished, deserialized.IsPublished); + Assert.Equal(expectedMetadata, deserialized.Metadata); + Assert.Equal(expectedName, deserialized.Name); + Assert.NotNull(deserialized.SelectedFieldsSchema); + Assert.Equal(expectedSelectedFieldsSchema.Count, deserialized.SelectedFieldsSchema.Count); + foreach (var item in expectedSelectedFieldsSchema) + { + Assert.True(deserialized.SelectedFieldsSchema.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.SelectedFieldsSchema[item.Key]); + } + Assert.Equal(expectedSize, deserialized.Size); + Assert.NotNull(deserialized.Tags); + Assert.Equal(expectedTags.Count, deserialized.Tags.Count); + for (int i = 0; i < expectedTags.Count; i++) + { + Assert.Equal(expectedTags[i], deserialized.Tags[i]); + } + Assert.Equal(expectedThumbnailUrl, deserialized.ThumbnailUrl); + Assert.Equal(expectedUrl, deserialized.Url); + Assert.Equal(expectedVersionInfo, deserialized.VersionInfo); + Assert.Equal(expectedVideoCodec, deserialized.VideoCodec); + Assert.Equal(expectedWidth, deserialized.Width); + } + + [Fact] + public void Validation_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEventData + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEventData + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + CustomCoordinates = "customCoordinates", + Tags = ["string"], + }; + + Assert.Null(model.AudioCodec); + Assert.False(model.RawData.ContainsKey("audioCodec")); + Assert.Null(model.BitRate); + Assert.False(model.RawData.ContainsKey("bitRate")); + Assert.Null(model.CustomMetadata); + Assert.False(model.RawData.ContainsKey("customMetadata")); + Assert.Null(model.Description); + Assert.False(model.RawData.ContainsKey("description")); + Assert.Null(model.Duration); + Assert.False(model.RawData.ContainsKey("duration")); + Assert.Null(model.EmbeddedMetadata); + Assert.False(model.RawData.ContainsKey("embeddedMetadata")); + Assert.Null(model.ExtensionStatus); + Assert.False(model.RawData.ContainsKey("extensionStatus")); + Assert.Null(model.FileID); + Assert.False(model.RawData.ContainsKey("fileId")); + Assert.Null(model.FilePath); + Assert.False(model.RawData.ContainsKey("filePath")); + Assert.Null(model.FileType); + Assert.False(model.RawData.ContainsKey("fileType")); + Assert.Null(model.Height); + Assert.False(model.RawData.ContainsKey("height")); + Assert.Null(model.IsPrivateFile); + Assert.False(model.RawData.ContainsKey("isPrivateFile")); + Assert.Null(model.IsPublished); + Assert.False(model.RawData.ContainsKey("isPublished")); + Assert.Null(model.Metadata); + Assert.False(model.RawData.ContainsKey("metadata")); + Assert.Null(model.Name); + Assert.False(model.RawData.ContainsKey("name")); + Assert.Null(model.SelectedFieldsSchema); + Assert.False(model.RawData.ContainsKey("selectedFieldsSchema")); + Assert.Null(model.Size); + Assert.False(model.RawData.ContainsKey("size")); + Assert.Null(model.ThumbnailUrl); + Assert.False(model.RawData.ContainsKey("thumbnailUrl")); + Assert.Null(model.Url); + Assert.False(model.RawData.ContainsKey("url")); + Assert.Null(model.VersionInfo); + Assert.False(model.RawData.ContainsKey("versionInfo")); + Assert.Null(model.VideoCodec); + Assert.False(model.RawData.ContainsKey("videoCodec")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEventData + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + CustomCoordinates = "customCoordinates", + Tags = ["string"], + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEventData + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + CustomCoordinates = "customCoordinates", + Tags = ["string"], + + // Null should be interpreted as omitted for these properties + AudioCodec = null, + BitRate = null, + CustomMetadata = null, + Description = null, + Duration = null, + EmbeddedMetadata = null, + ExtensionStatus = null, + FileID = null, + FilePath = null, + FileType = null, + Height = null, + IsPrivateFile = null, + IsPublished = null, + Metadata = null, + Name = null, + SelectedFieldsSchema = null, + Size = null, + ThumbnailUrl = null, + Url = null, + VersionInfo = null, + VideoCodec = null, + Width = null, + }; + + Assert.Null(model.AudioCodec); + Assert.False(model.RawData.ContainsKey("audioCodec")); + Assert.Null(model.BitRate); + Assert.False(model.RawData.ContainsKey("bitRate")); + Assert.Null(model.CustomMetadata); + Assert.False(model.RawData.ContainsKey("customMetadata")); + Assert.Null(model.Description); + Assert.False(model.RawData.ContainsKey("description")); + Assert.Null(model.Duration); + Assert.False(model.RawData.ContainsKey("duration")); + Assert.Null(model.EmbeddedMetadata); + Assert.False(model.RawData.ContainsKey("embeddedMetadata")); + Assert.Null(model.ExtensionStatus); + Assert.False(model.RawData.ContainsKey("extensionStatus")); + Assert.Null(model.FileID); + Assert.False(model.RawData.ContainsKey("fileId")); + Assert.Null(model.FilePath); + Assert.False(model.RawData.ContainsKey("filePath")); + Assert.Null(model.FileType); + Assert.False(model.RawData.ContainsKey("fileType")); + Assert.Null(model.Height); + Assert.False(model.RawData.ContainsKey("height")); + Assert.Null(model.IsPrivateFile); + Assert.False(model.RawData.ContainsKey("isPrivateFile")); + Assert.Null(model.IsPublished); + Assert.False(model.RawData.ContainsKey("isPublished")); + Assert.Null(model.Metadata); + Assert.False(model.RawData.ContainsKey("metadata")); + Assert.Null(model.Name); + Assert.False(model.RawData.ContainsKey("name")); + Assert.Null(model.SelectedFieldsSchema); + Assert.False(model.RawData.ContainsKey("selectedFieldsSchema")); + Assert.Null(model.Size); + Assert.False(model.RawData.ContainsKey("size")); + Assert.Null(model.ThumbnailUrl); + Assert.False(model.RawData.ContainsKey("thumbnailUrl")); + Assert.Null(model.Url); + Assert.False(model.RawData.ContainsKey("url")); + Assert.Null(model.VersionInfo); + Assert.False(model.RawData.ContainsKey("versionInfo")); + Assert.Null(model.VideoCodec); + Assert.False(model.RawData.ContainsKey("videoCodec")); + Assert.Null(model.Width); + Assert.False(model.RawData.ContainsKey("width")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEventData + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + CustomCoordinates = "customCoordinates", + Tags = ["string"], + + // Null should be interpreted as omitted for these properties + AudioCodec = null, + BitRate = null, + CustomMetadata = null, + Description = null, + Duration = null, + EmbeddedMetadata = null, + ExtensionStatus = null, + FileID = null, + FilePath = null, + FileType = null, + Height = null, + IsPrivateFile = null, + IsPublished = null, + Metadata = null, + Name = null, + SelectedFieldsSchema = null, + Size = null, + ThumbnailUrl = null, + Url = null, + VersionInfo = null, + VideoCodec = null, + Width = null, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNullablePropertiesUnsetAreNotSet_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEventData + { + AudioCodec = "audioCodec", + BitRate = 0, + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + Assert.Null(model.AITags); + Assert.False(model.RawData.ContainsKey("AITags")); + Assert.Null(model.CustomCoordinates); + Assert.False(model.RawData.ContainsKey("customCoordinates")); + Assert.Null(model.Tags); + Assert.False(model.RawData.ContainsKey("tags")); + } + + [Fact] + public void OptionalNullablePropertiesUnsetValidation_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEventData + { + AudioCodec = "audioCodec", + BitRate = 0, + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNullablePropertiesSetToNullAreSetToNull_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEventData + { + AudioCodec = "audioCodec", + BitRate = 0, + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + + AITags = null, + CustomCoordinates = null, + Tags = null, + }; + + Assert.Null(model.AITags); + Assert.True(model.RawData.ContainsKey("AITags")); + Assert.Null(model.CustomCoordinates); + Assert.True(model.RawData.ContainsKey("customCoordinates")); + Assert.Null(model.Tags); + Assert.True(model.RawData.ContainsKey("tags")); + } + + [Fact] + public void OptionalNullablePropertiesSetToNullValidation_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEventData + { + AudioCodec = "audioCodec", + BitRate = 0, + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + + AITags = null, + CustomCoordinates = null, + Tags = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEventData + { + AITags = + [ + new() + { + Confidence = 0, + Name = "name", + Source = "source", + }, + ], + AudioCodec = "audioCodec", + BitRate = 0, + CustomCoordinates = "customCoordinates", + CustomMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Duration = 0, + EmbeddedMetadata = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + ExtensionStatus = new() + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }, + FileID = "fileId", + FilePath = "filePath", + FileType = "fileType", + Height = 0, + IsPrivateFile = true, + IsPublished = true, + Metadata = new() + { + AudioCodec = "audioCodec", + BitRate = 0, + Density = 0, + Duration = 0, + Exif = new() + { + ExifValue = new() + { + ApertureValue = 0, + ColorSpace = 0, + CreateDate = "CreateDate", + CustomRendered = 0, + DateTimeOriginal = "DateTimeOriginal", + ExifImageHeight = 0, + ExifImageWidth = 0, + ExifVersion = "ExifVersion", + ExposureCompensation = 0, + ExposureMode = 0, + ExposureProgram = 0, + ExposureTime = 0, + Flash = 0, + FlashpixVersion = "FlashpixVersion", + FNumber = 0, + FocalLength = 0, + FocalPlaneResolutionUnit = 0, + FocalPlaneXResolution = 0, + FocalPlaneYResolution = 0, + InteropOffset = 0, + Iso = 0, + MeteringMode = 0, + SceneCaptureType = 0, + ShutterSpeedValue = 0, + SubSecTime = "SubSecTime", + WhiteBalance = 0, + }, + Gps = new() { GpsVersionID = [0] }, + Image = new() + { + ExifOffset = 0, + GpsInfo = 0, + Make = "Make", + Model = "Model", + ModifyDate = "ModifyDate", + Orientation = 0, + ResolutionUnit = 0, + Software = "Software", + XResolution = 0, + YCbCrPositioning = 0, + YResolution = 0, + }, + Interoperability = new() + { + InteropIndex = "InteropIndex", + InteropVersion = "InteropVersion", + }, + Makernote = new Dictionary() + { + { "foo", JsonSerializer.SerializeToElement("bar") }, + }, + Thumbnail = new() + { + Compression = 0, + ResolutionUnit = 0, + ThumbnailLength = 0, + ThumbnailOffset = 0, + XResolution = 0, + YResolution = 0, + }, + }, + Format = "format", + HasColorProfile = true, + HasTransparency = true, + Height = 0, + PHash = "pHash", + Quality = 0, + Size = 0, + VideoCodec = "videoCodec", + Width = 0, + }, + Name = "name", + SelectedFieldsSchema = new Dictionary() + { + { + "foo", + new() + { + Type = Models::Type.Text, + DefaultValue = new( + [ + new Models::DefaultValueArrayItem(true), + new Models::DefaultValueArrayItem(10), + new Models::DefaultValueArrayItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = "string", + MinLength = 0, + MinValue = "string", + ReadOnly = true, + SelectOptions = ["small", "medium", "large", 30, 40, true], + SelectOptionsTruncated = true, + } + }, + }, + Size = 0, + Tags = ["string"], + ThumbnailUrl = "thumbnailUrl", + Url = "url", + VersionInfo = new() { ID = "id", Name = "name" }, + VideoCodec = "videoCodec", + Width = 0, + }; + + UploadPreTransformSuccessEventUploadPreTransformSuccessEventData copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExtensionStatusTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExtensionStatus + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }; + + ApiEnum expectedAIAutoDescription = AIAutoDescription.Success; + ApiEnum expectedAITasks = AITasks.Success; + ApiEnum expectedAwsAutoTagging = AwsAutoTagging.Success; + ApiEnum expectedGoogleAutoTagging = GoogleAutoTagging.Success; + ApiEnum expectedRemoveBg = RemoveBg.Success; + + Assert.Equal(expectedAIAutoDescription, model.AIAutoDescription); + Assert.Equal(expectedAITasks, model.AITasks); + Assert.Equal(expectedAwsAutoTagging, model.AwsAutoTagging); + Assert.Equal(expectedGoogleAutoTagging, model.GoogleAutoTagging); + Assert.Equal(expectedRemoveBg, model.RemoveBg); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExtensionStatus + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExtensionStatus + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum expectedAIAutoDescription = AIAutoDescription.Success; + ApiEnum expectedAITasks = AITasks.Success; + ApiEnum expectedAwsAutoTagging = AwsAutoTagging.Success; + ApiEnum expectedGoogleAutoTagging = GoogleAutoTagging.Success; + ApiEnum expectedRemoveBg = RemoveBg.Success; + + Assert.Equal(expectedAIAutoDescription, deserialized.AIAutoDescription); + Assert.Equal(expectedAITasks, deserialized.AITasks); + Assert.Equal(expectedAwsAutoTagging, deserialized.AwsAutoTagging); + Assert.Equal(expectedGoogleAutoTagging, deserialized.GoogleAutoTagging); + Assert.Equal(expectedRemoveBg, deserialized.RemoveBg); + } + + [Fact] + public void Validation_Works() + { + var model = new ExtensionStatus + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ExtensionStatus { }; + + Assert.Null(model.AIAutoDescription); + Assert.False(model.RawData.ContainsKey("ai-auto-description")); + Assert.Null(model.AITasks); + Assert.False(model.RawData.ContainsKey("ai-tasks")); + Assert.Null(model.AwsAutoTagging); + Assert.False(model.RawData.ContainsKey("aws-auto-tagging")); + Assert.Null(model.GoogleAutoTagging); + Assert.False(model.RawData.ContainsKey("google-auto-tagging")); + Assert.Null(model.RemoveBg); + Assert.False(model.RawData.ContainsKey("remove-bg")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new ExtensionStatus { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ExtensionStatus + { + // Null should be interpreted as omitted for these properties + AIAutoDescription = null, + AITasks = null, + AwsAutoTagging = null, + GoogleAutoTagging = null, + RemoveBg = null, + }; + + Assert.Null(model.AIAutoDescription); + Assert.False(model.RawData.ContainsKey("ai-auto-description")); + Assert.Null(model.AITasks); + Assert.False(model.RawData.ContainsKey("ai-tasks")); + Assert.Null(model.AwsAutoTagging); + Assert.False(model.RawData.ContainsKey("aws-auto-tagging")); + Assert.Null(model.GoogleAutoTagging); + Assert.False(model.RawData.ContainsKey("google-auto-tagging")); + Assert.Null(model.RemoveBg); + Assert.False(model.RawData.ContainsKey("remove-bg")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new ExtensionStatus + { + // Null should be interpreted as omitted for these properties + AIAutoDescription = null, + AITasks = null, + AwsAutoTagging = null, + GoogleAutoTagging = null, + RemoveBg = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExtensionStatus + { + AIAutoDescription = AIAutoDescription.Success, + AITasks = AITasks.Success, + AwsAutoTagging = AwsAutoTagging.Success, + GoogleAutoTagging = GoogleAutoTagging.Success, + RemoveBg = RemoveBg.Success, + }; + + ExtensionStatus copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class AIAutoDescriptionTest : TestBase +{ + [Theory] + [InlineData(AIAutoDescription.Success)] + [InlineData(AIAutoDescription.Pending)] + [InlineData(AIAutoDescription.Failed)] + public void Validation_Works(AIAutoDescription rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(AIAutoDescription.Success)] + [InlineData(AIAutoDescription.Pending)] + [InlineData(AIAutoDescription.Failed)] + public void SerializationRoundtrip_Works(AIAutoDescription rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class AITasksTest : TestBase +{ + [Theory] + [InlineData(AITasks.Success)] + [InlineData(AITasks.Pending)] + [InlineData(AITasks.Failed)] + public void Validation_Works(AITasks rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(AITasks.Success)] + [InlineData(AITasks.Pending)] + [InlineData(AITasks.Failed)] + public void SerializationRoundtrip_Works(AITasks rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class AwsAutoTaggingTest : TestBase +{ + [Theory] + [InlineData(AwsAutoTagging.Success)] + [InlineData(AwsAutoTagging.Pending)] + [InlineData(AwsAutoTagging.Failed)] + public void Validation_Works(AwsAutoTagging rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(AwsAutoTagging.Success)] + [InlineData(AwsAutoTagging.Pending)] + [InlineData(AwsAutoTagging.Failed)] + public void SerializationRoundtrip_Works(AwsAutoTagging rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class GoogleAutoTaggingTest : TestBase +{ + [Theory] + [InlineData(GoogleAutoTagging.Success)] + [InlineData(GoogleAutoTagging.Pending)] + [InlineData(GoogleAutoTagging.Failed)] + public void Validation_Works(GoogleAutoTagging rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(GoogleAutoTagging.Success)] + [InlineData(GoogleAutoTagging.Pending)] + [InlineData(GoogleAutoTagging.Failed)] + public void SerializationRoundtrip_Works(GoogleAutoTagging rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class RemoveBgTest : TestBase +{ + [Theory] + [InlineData(RemoveBg.Success)] + [InlineData(RemoveBg.Pending)] + [InlineData(RemoveBg.Failed)] + public void Validation_Works(RemoveBg rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(RemoveBg.Success)] + [InlineData(RemoveBg.Pending)] + [InlineData(RemoveBg.Failed)] + public void SerializationRoundtrip_Works(RemoveBg rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequestTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest + { + Transformation = "transformation", + XRequestID = "x_request_id", + }; + + string expectedTransformation = "transformation"; + string expectedXRequestID = "x_request_id"; + + Assert.Equal(expectedTransformation, model.Transformation); + Assert.Equal(expectedXRequestID, model.XRequestID); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest + { + Transformation = "transformation", + XRequestID = "x_request_id", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest + { + Transformation = "transformation", + XRequestID = "x_request_id", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedTransformation = "transformation"; + string expectedXRequestID = "x_request_id"; + + Assert.Equal(expectedTransformation, deserialized.Transformation); + Assert.Equal(expectedXRequestID, deserialized.XRequestID); + } + + [Fact] + public void Validation_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest + { + Transformation = "transformation", + XRequestID = "x_request_id", + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest + { + Transformation = "transformation", + XRequestID = "x_request_id", + }; + + UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Webhooks/VideoTransformationAcceptedEventTest.cs b/src/Imagekit.Tests/Models/Webhooks/VideoTransformationAcceptedEventTest.cs new file mode 100644 index 00000000..6d8f11ac --- /dev/null +++ b/src/Imagekit.Tests/Models/Webhooks/VideoTransformationAcceptedEventTest.cs @@ -0,0 +1,1727 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Webhooks; + +namespace Imagekit.Tests.Models.Webhooks; + +public class VideoTransformationAcceptedEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new VideoTransformationAcceptedEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + VideoTransformationAcceptedEventVideoTransformationAcceptedEventData expectedData = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }, + }; + VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest expectedRequest = + new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + Assert.Equal(expectedRequest, model.Request); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new VideoTransformationAcceptedEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new VideoTransformationAcceptedEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + VideoTransformationAcceptedEventVideoTransformationAcceptedEventData expectedData = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }, + }; + VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest expectedRequest = + new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + Assert.Equal(expectedRequest, deserialized.Request); + } + + [Fact] + public void Validation_Works() + { + var model = new VideoTransformationAcceptedEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new VideoTransformationAcceptedEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + VideoTransformationAcceptedEvent copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class VideoTransformationAcceptedEventVideoTransformationAcceptedEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new VideoTransformationAcceptedEventVideoTransformationAcceptedEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + VideoTransformationAcceptedEventVideoTransformationAcceptedEventData expectedData = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }, + }; + VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest expectedRequest = + new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement( + "video.transformation.accepted" + ); + + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + Assert.Equal(expectedRequest, model.Request); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new VideoTransformationAcceptedEventVideoTransformationAcceptedEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new VideoTransformationAcceptedEventVideoTransformationAcceptedEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + VideoTransformationAcceptedEventVideoTransformationAcceptedEventData expectedData = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }, + }; + VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest expectedRequest = + new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement( + "video.transformation.accepted" + ); + + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + Assert.Equal(expectedRequest, deserialized.Request); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + } + + [Fact] + public void Validation_Works() + { + var model = new VideoTransformationAcceptedEventVideoTransformationAcceptedEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new VideoTransformationAcceptedEventVideoTransformationAcceptedEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + VideoTransformationAcceptedEventVideoTransformationAcceptedEvent copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new VideoTransformationAcceptedEventVideoTransformationAcceptedEventData + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }, + }; + + Asset expectedAsset = new("https://example.com"); + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation expectedTransformation = + new() + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }; + + Assert.Equal(expectedAsset, model.Asset); + Assert.Equal(expectedTransformation, model.Transformation); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new VideoTransformationAcceptedEventVideoTransformationAcceptedEventData + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new VideoTransformationAcceptedEventVideoTransformationAcceptedEventData + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + Asset expectedAsset = new("https://example.com"); + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation expectedTransformation = + new() + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }; + + Assert.Equal(expectedAsset, deserialized.Asset); + Assert.Equal(expectedTransformation, deserialized.Transformation); + } + + [Fact] + public void Validation_Works() + { + var model = new VideoTransformationAcceptedEventVideoTransformationAcceptedEventData + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new VideoTransformationAcceptedEventVideoTransformationAcceptedEventData + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }, + }; + + VideoTransformationAcceptedEventVideoTransformationAcceptedEventData copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class AssetTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Asset { Url = "https://example.com" }; + + string expectedUrl = "https://example.com"; + + Assert.Equal(expectedUrl, model.Url); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Asset { Url = "https://example.com" }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Asset { Url = "https://example.com" }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + Assert.NotNull(deserialized); + + string expectedUrl = "https://example.com"; + + Assert.Equal(expectedUrl, deserialized.Url); + } + + [Fact] + public void Validation_Works() + { + var model = new Asset { Url = "https://example.com" }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Asset { Url = "https://example.com" }; + + Asset copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationTest + : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = + new VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }; + + ApiEnum< + string, + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType + > expectedType = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation; + Options expectedOptions = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }; + + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedOptions, model.Options); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = + new VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = + new VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum< + string, + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType + > expectedType = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation; + Options expectedOptions = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }; + + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedOptions, deserialized.Options); + } + + [Fact] + public void Validation_Works() + { + var model = + new VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = + new VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + }; + + Assert.Null(model.Options); + Assert.False(model.RawData.ContainsKey("options")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = + new VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = + new VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + + // Null should be interpreted as omitted for these properties + Options = null, + }; + + Assert.Null(model.Options); + Assert.False(model.RawData.ContainsKey("options")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = + new VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + + // Null should be interpreted as omitted for these properties + Options = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = + new VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation + { + Type = + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }, + }; + + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation copied = + new(model); + + Assert.Equal(model, copied); + } +} + +public class VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationTypeTest + : TestBase +{ + [Theory] + [InlineData( + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation + )] + [InlineData( + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.GifToVideo + )] + [InlineData( + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoThumbnail + )] + public void Validation_Works( + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType + > value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData( + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation + )] + [InlineData( + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.GifToVideo + )] + [InlineData( + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoThumbnail + )] + public void SerializationRoundtrip_Works( + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType + > value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class OptionsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Options + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }; + + ApiEnum expectedAudioCodec = AudioCodec.Aac; + bool expectedAutoRotate = true; + ApiEnum expectedFormat = Format.Mp4; + long expectedQuality = 0; + ApiEnum expectedStreamProtocol = StreamProtocol.Hls; + List expectedVariants = ["string"]; + ApiEnum expectedVideoCodec = VideoCodec.H264; + + Assert.Equal(expectedAudioCodec, model.AudioCodec); + Assert.Equal(expectedAutoRotate, model.AutoRotate); + Assert.Equal(expectedFormat, model.Format); + Assert.Equal(expectedQuality, model.Quality); + Assert.Equal(expectedStreamProtocol, model.StreamProtocol); + Assert.NotNull(model.Variants); + Assert.Equal(expectedVariants.Count, model.Variants.Count); + for (int i = 0; i < expectedVariants.Count; i++) + { + Assert.Equal(expectedVariants[i], model.Variants[i]); + } + Assert.Equal(expectedVideoCodec, model.VideoCodec); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Options + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Options + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum expectedAudioCodec = AudioCodec.Aac; + bool expectedAutoRotate = true; + ApiEnum expectedFormat = Format.Mp4; + long expectedQuality = 0; + ApiEnum expectedStreamProtocol = StreamProtocol.Hls; + List expectedVariants = ["string"]; + ApiEnum expectedVideoCodec = VideoCodec.H264; + + Assert.Equal(expectedAudioCodec, deserialized.AudioCodec); + Assert.Equal(expectedAutoRotate, deserialized.AutoRotate); + Assert.Equal(expectedFormat, deserialized.Format); + Assert.Equal(expectedQuality, deserialized.Quality); + Assert.Equal(expectedStreamProtocol, deserialized.StreamProtocol); + Assert.NotNull(deserialized.Variants); + Assert.Equal(expectedVariants.Count, deserialized.Variants.Count); + for (int i = 0; i < expectedVariants.Count; i++) + { + Assert.Equal(expectedVariants[i], deserialized.Variants[i]); + } + Assert.Equal(expectedVideoCodec, deserialized.VideoCodec); + } + + [Fact] + public void Validation_Works() + { + var model = new Options + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Options { }; + + Assert.Null(model.AudioCodec); + Assert.False(model.RawData.ContainsKey("audio_codec")); + Assert.Null(model.AutoRotate); + Assert.False(model.RawData.ContainsKey("auto_rotate")); + Assert.Null(model.Format); + Assert.False(model.RawData.ContainsKey("format")); + Assert.Null(model.Quality); + Assert.False(model.RawData.ContainsKey("quality")); + Assert.Null(model.StreamProtocol); + Assert.False(model.RawData.ContainsKey("stream_protocol")); + Assert.Null(model.Variants); + Assert.False(model.RawData.ContainsKey("variants")); + Assert.Null(model.VideoCodec); + Assert.False(model.RawData.ContainsKey("video_codec")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Options { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Options + { + // Null should be interpreted as omitted for these properties + AudioCodec = null, + AutoRotate = null, + Format = null, + Quality = null, + StreamProtocol = null, + Variants = null, + VideoCodec = null, + }; + + Assert.Null(model.AudioCodec); + Assert.False(model.RawData.ContainsKey("audio_codec")); + Assert.Null(model.AutoRotate); + Assert.False(model.RawData.ContainsKey("auto_rotate")); + Assert.Null(model.Format); + Assert.False(model.RawData.ContainsKey("format")); + Assert.Null(model.Quality); + Assert.False(model.RawData.ContainsKey("quality")); + Assert.Null(model.StreamProtocol); + Assert.False(model.RawData.ContainsKey("stream_protocol")); + Assert.Null(model.Variants); + Assert.False(model.RawData.ContainsKey("variants")); + Assert.Null(model.VideoCodec); + Assert.False(model.RawData.ContainsKey("video_codec")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Options + { + // Null should be interpreted as omitted for these properties + AudioCodec = null, + AutoRotate = null, + Format = null, + Quality = null, + StreamProtocol = null, + Variants = null, + VideoCodec = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Options + { + AudioCodec = AudioCodec.Aac, + AutoRotate = true, + Format = Format.Mp4, + Quality = 0, + StreamProtocol = StreamProtocol.Hls, + Variants = ["string"], + VideoCodec = VideoCodec.H264, + }; + + Options copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class AudioCodecTest : TestBase +{ + [Theory] + [InlineData(AudioCodec.Aac)] + [InlineData(AudioCodec.Opus)] + public void Validation_Works(AudioCodec rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(AudioCodec.Aac)] + [InlineData(AudioCodec.Opus)] + public void SerializationRoundtrip_Works(AudioCodec rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class FormatTest : TestBase +{ + [Theory] + [InlineData(Format.Mp4)] + [InlineData(Format.Webm)] + [InlineData(Format.Jpg)] + [InlineData(Format.Png)] + [InlineData(Format.Webp)] + public void Validation_Works(Format rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Format.Mp4)] + [InlineData(Format.Webm)] + [InlineData(Format.Jpg)] + [InlineData(Format.Png)] + [InlineData(Format.Webp)] + public void SerializationRoundtrip_Works(Format rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class StreamProtocolTest : TestBase +{ + [Theory] + [InlineData(StreamProtocol.Hls)] + [InlineData(StreamProtocol.Dash)] + public void Validation_Works(StreamProtocol rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(StreamProtocol.Hls)] + [InlineData(StreamProtocol.Dash)] + public void SerializationRoundtrip_Works(StreamProtocol rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class VideoCodecTest : TestBase +{ + [Theory] + [InlineData(VideoCodec.H264)] + [InlineData(VideoCodec.Vp9)] + [InlineData(VideoCodec.Av1)] + public void Validation_Works(VideoCodec rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(VideoCodec.H264)] + [InlineData(VideoCodec.Vp9)] + [InlineData(VideoCodec.Av1)] + public void SerializationRoundtrip_Works(VideoCodec rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequestTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + + string expectedUrl = "https://example.com"; + string expectedXRequestID = "x_request_id"; + string expectedUserAgent = "user_agent"; + + Assert.Equal(expectedUrl, model.Url); + Assert.Equal(expectedXRequestID, model.XRequestID); + Assert.Equal(expectedUserAgent, model.UserAgent); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedUrl = "https://example.com"; + string expectedXRequestID = "x_request_id"; + string expectedUserAgent = "user_agent"; + + Assert.Equal(expectedUrl, deserialized.Url); + Assert.Equal(expectedXRequestID, deserialized.XRequestID); + Assert.Equal(expectedUserAgent, deserialized.UserAgent); + } + + [Fact] + public void Validation_Works() + { + var model = new VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + }; + + Assert.Null(model.UserAgent); + Assert.False(model.RawData.ContainsKey("user_agent")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + + // Null should be interpreted as omitted for these properties + UserAgent = null, + }; + + Assert.Null(model.UserAgent); + Assert.False(model.RawData.ContainsKey("user_agent")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + + // Null should be interpreted as omitted for these properties + UserAgent = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + + VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Webhooks/VideoTransformationErrorEventTest.cs b/src/Imagekit.Tests/Models/Webhooks/VideoTransformationErrorEventTest.cs new file mode 100644 index 00000000..2785a40f --- /dev/null +++ b/src/Imagekit.Tests/Models/Webhooks/VideoTransformationErrorEventTest.cs @@ -0,0 +1,2230 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Webhooks; + +namespace Imagekit.Tests.Models.Webhooks; + +public class VideoTransformationErrorEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new VideoTransformationErrorEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + VideoTransformationErrorEventVideoTransformationErrorEventData expectedData = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }; + VideoTransformationErrorEventVideoTransformationErrorEventRequest expectedRequest = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + Assert.Equal(expectedRequest, model.Request); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new VideoTransformationErrorEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new VideoTransformationErrorEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + VideoTransformationErrorEventVideoTransformationErrorEventData expectedData = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }; + VideoTransformationErrorEventVideoTransformationErrorEventRequest expectedRequest = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + Assert.Equal(expectedRequest, deserialized.Request); + } + + [Fact] + public void Validation_Works() + { + var model = new VideoTransformationErrorEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new VideoTransformationErrorEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + VideoTransformationErrorEvent copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class VideoTransformationErrorEventVideoTransformationErrorEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + VideoTransformationErrorEventVideoTransformationErrorEventData expectedData = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }; + VideoTransformationErrorEventVideoTransformationErrorEventRequest expectedRequest = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("video.transformation.error"); + + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + Assert.Equal(expectedRequest, model.Request); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + VideoTransformationErrorEventVideoTransformationErrorEventData expectedData = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }; + VideoTransformationErrorEventVideoTransformationErrorEventRequest expectedRequest = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("video.transformation.error"); + + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + Assert.Equal(expectedRequest, deserialized.Request); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + } + + [Fact] + public void Validation_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + VideoTransformationErrorEventVideoTransformationErrorEvent copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class VideoTransformationErrorEventVideoTransformationErrorEventDataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventData + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }; + + VideoTransformationErrorEventVideoTransformationErrorEventDataAsset expectedAsset = new( + "https://example.com" + ); + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation expectedTransformation = + new() + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }; + + Assert.Equal(expectedAsset, model.Asset); + Assert.Equal(expectedTransformation, model.Transformation); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventData + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventData + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + VideoTransformationErrorEventVideoTransformationErrorEventDataAsset expectedAsset = new( + "https://example.com" + ); + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation expectedTransformation = + new() + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }; + + Assert.Equal(expectedAsset, deserialized.Asset); + Assert.Equal(expectedTransformation, deserialized.Transformation); + } + + [Fact] + public void Validation_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventData + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventData + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }, + }; + + VideoTransformationErrorEventVideoTransformationErrorEventData copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class VideoTransformationErrorEventVideoTransformationErrorEventDataAssetTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventDataAsset + { + Url = "https://example.com", + }; + + string expectedUrl = "https://example.com"; + + Assert.Equal(expectedUrl, model.Url); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventDataAsset + { + Url = "https://example.com", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventDataAsset + { + Url = "https://example.com", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedUrl = "https://example.com"; + + Assert.Equal(expectedUrl, deserialized.Url); + } + + [Fact] + public void Validation_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventDataAsset + { + Url = "https://example.com", + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventDataAsset + { + Url = "https://example.com", + }; + + VideoTransformationErrorEventVideoTransformationErrorEventDataAsset copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationTest + : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }; + + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType + > expectedType = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation; + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationError expectedError = + new(Reason.EncodingFailed); + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions expectedOptions = + new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }; + + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedError, model.Error); + Assert.Equal(expectedOptions, model.Options); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType + > expectedType = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation; + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationError expectedError = + new(Reason.EncodingFailed); + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions expectedOptions = + new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }; + + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedError, deserialized.Error); + Assert.Equal(expectedOptions, deserialized.Options); + } + + [Fact] + public void Validation_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + }; + + Assert.Null(model.Error); + Assert.False(model.RawData.ContainsKey("error")); + Assert.Null(model.Options); + Assert.False(model.RawData.ContainsKey("options")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + + // Null should be interpreted as omitted for these properties + Error = null, + Options = null, + }; + + Assert.Null(model.Error); + Assert.False(model.RawData.ContainsKey("error")); + Assert.Null(model.Options); + Assert.False(model.RawData.ContainsKey("options")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + + // Null should be interpreted as omitted for these properties + Error = null, + Options = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation + { + Type = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + Error = new(Reason.EncodingFailed), + Options = new() + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }, + }; + + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation copied = new( + model + ); + + Assert.Equal(model, copied); + } +} + +public class VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationTypeTest + : TestBase +{ + [Theory] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation + )] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.GifToVideo + )] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoThumbnail + )] + public void Validation_Works( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType + > value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation + )] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.GifToVideo + )] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoThumbnail + )] + public void SerializationRoundtrip_Works( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType + > value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationErrorTest + : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = + new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationError + { + Reason = Reason.EncodingFailed, + }; + + ApiEnum expectedReason = Reason.EncodingFailed; + + Assert.Equal(expectedReason, model.Reason); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = + new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationError + { + Reason = Reason.EncodingFailed, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = + new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationError + { + Reason = Reason.EncodingFailed, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum expectedReason = Reason.EncodingFailed; + + Assert.Equal(expectedReason, deserialized.Reason); + } + + [Fact] + public void Validation_Works() + { + var model = + new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationError + { + Reason = Reason.EncodingFailed, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = + new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationError + { + Reason = Reason.EncodingFailed, + }; + + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationError copied = + new(model); + + Assert.Equal(model, copied); + } +} + +public class ReasonTest : TestBase +{ + [Theory] + [InlineData(Reason.EncodingFailed)] + [InlineData(Reason.DownloadFailed)] + [InlineData(Reason.InternalServerError)] + public void Validation_Works(Reason rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Reason.EncodingFailed)] + [InlineData(Reason.DownloadFailed)] + [InlineData(Reason.InternalServerError)] + public void SerializationRoundtrip_Works(Reason rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsTest + : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = + new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }; + + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec + > expectedAudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac; + bool expectedAutoRotate = true; + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat + > expectedFormat = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4; + long expectedQuality = 0; + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol + > expectedStreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls; + List expectedVariants = ["string"]; + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec + > expectedVideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264; + + Assert.Equal(expectedAudioCodec, model.AudioCodec); + Assert.Equal(expectedAutoRotate, model.AutoRotate); + Assert.Equal(expectedFormat, model.Format); + Assert.Equal(expectedQuality, model.Quality); + Assert.Equal(expectedStreamProtocol, model.StreamProtocol); + Assert.NotNull(model.Variants); + Assert.Equal(expectedVariants.Count, model.Variants.Count); + for (int i = 0; i < expectedVariants.Count; i++) + { + Assert.Equal(expectedVariants[i], model.Variants[i]); + } + Assert.Equal(expectedVideoCodec, model.VideoCodec); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = + new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = + new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec + > expectedAudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac; + bool expectedAutoRotate = true; + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat + > expectedFormat = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4; + long expectedQuality = 0; + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol + > expectedStreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls; + List expectedVariants = ["string"]; + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec + > expectedVideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264; + + Assert.Equal(expectedAudioCodec, deserialized.AudioCodec); + Assert.Equal(expectedAutoRotate, deserialized.AutoRotate); + Assert.Equal(expectedFormat, deserialized.Format); + Assert.Equal(expectedQuality, deserialized.Quality); + Assert.Equal(expectedStreamProtocol, deserialized.StreamProtocol); + Assert.NotNull(deserialized.Variants); + Assert.Equal(expectedVariants.Count, deserialized.Variants.Count); + for (int i = 0; i < expectedVariants.Count; i++) + { + Assert.Equal(expectedVariants[i], deserialized.Variants[i]); + } + Assert.Equal(expectedVideoCodec, deserialized.VideoCodec); + } + + [Fact] + public void Validation_Works() + { + var model = + new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = + new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions + { }; + + Assert.Null(model.AudioCodec); + Assert.False(model.RawData.ContainsKey("audio_codec")); + Assert.Null(model.AutoRotate); + Assert.False(model.RawData.ContainsKey("auto_rotate")); + Assert.Null(model.Format); + Assert.False(model.RawData.ContainsKey("format")); + Assert.Null(model.Quality); + Assert.False(model.RawData.ContainsKey("quality")); + Assert.Null(model.StreamProtocol); + Assert.False(model.RawData.ContainsKey("stream_protocol")); + Assert.Null(model.Variants); + Assert.False(model.RawData.ContainsKey("variants")); + Assert.Null(model.VideoCodec); + Assert.False(model.RawData.ContainsKey("video_codec")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = + new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions + { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = + new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions + { + // Null should be interpreted as omitted for these properties + AudioCodec = null, + AutoRotate = null, + Format = null, + Quality = null, + StreamProtocol = null, + Variants = null, + VideoCodec = null, + }; + + Assert.Null(model.AudioCodec); + Assert.False(model.RawData.ContainsKey("audio_codec")); + Assert.Null(model.AutoRotate); + Assert.False(model.RawData.ContainsKey("auto_rotate")); + Assert.Null(model.Format); + Assert.False(model.RawData.ContainsKey("format")); + Assert.Null(model.Quality); + Assert.False(model.RawData.ContainsKey("quality")); + Assert.Null(model.StreamProtocol); + Assert.False(model.RawData.ContainsKey("stream_protocol")); + Assert.Null(model.Variants); + Assert.False(model.RawData.ContainsKey("variants")); + Assert.Null(model.VideoCodec); + Assert.False(model.RawData.ContainsKey("video_codec")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = + new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions + { + // Null should be interpreted as omitted for these properties + AudioCodec = null, + AutoRotate = null, + Format = null, + Quality = null, + StreamProtocol = null, + Variants = null, + VideoCodec = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = + new VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions + { + AudioCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + }; + + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions copied = + new(model); + + Assert.Equal(model, copied); + } +} + +public class VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodecTest + : TestBase +{ + [Theory] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac + )] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Opus + )] + public void Validation_Works( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec + > value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac + )] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Opus + )] + public void SerializationRoundtrip_Works( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec + > value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormatTest + : TestBase +{ + [Theory] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4 + )] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Webm + )] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Jpg + )] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Png + )] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Webp + )] + public void Validation_Works( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat + > value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4 + )] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Webm + )] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Jpg + )] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Png + )] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Webp + )] + public void SerializationRoundtrip_Works( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat + > value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocolTest + : TestBase +{ + [Theory] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls + )] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Dash + )] + public void Validation_Works( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol + > value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls + )] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Dash + )] + public void SerializationRoundtrip_Works( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol + > value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodecTest + : TestBase +{ + [Theory] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264 + )] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.Vp9 + )] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.Av1 + )] + public void Validation_Works( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec + > value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264 + )] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.Vp9 + )] + [InlineData( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.Av1 + )] + public void SerializationRoundtrip_Works( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec + > value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class VideoTransformationErrorEventVideoTransformationErrorEventRequestTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + + string expectedUrl = "https://example.com"; + string expectedXRequestID = "x_request_id"; + string expectedUserAgent = "user_agent"; + + Assert.Equal(expectedUrl, model.Url); + Assert.Equal(expectedXRequestID, model.XRequestID); + Assert.Equal(expectedUserAgent, model.UserAgent); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedUrl = "https://example.com"; + string expectedXRequestID = "x_request_id"; + string expectedUserAgent = "user_agent"; + + Assert.Equal(expectedUrl, deserialized.Url); + Assert.Equal(expectedXRequestID, deserialized.XRequestID); + Assert.Equal(expectedUserAgent, deserialized.UserAgent); + } + + [Fact] + public void Validation_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + }; + + Assert.Null(model.UserAgent); + Assert.False(model.RawData.ContainsKey("user_agent")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + + // Null should be interpreted as omitted for these properties + UserAgent = null, + }; + + Assert.Null(model.UserAgent); + Assert.False(model.RawData.ContainsKey("user_agent")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + + // Null should be interpreted as omitted for these properties + UserAgent = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new VideoTransformationErrorEventVideoTransformationErrorEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + + VideoTransformationErrorEventVideoTransformationErrorEventRequest copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/Models/Webhooks/VideoTransformationReadyEventTest.cs b/src/Imagekit.Tests/Models/Webhooks/VideoTransformationReadyEventTest.cs new file mode 100644 index 00000000..3fd282ac --- /dev/null +++ b/src/Imagekit.Tests/Models/Webhooks/VideoTransformationReadyEventTest.cs @@ -0,0 +1,3195 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Webhooks; + +namespace Imagekit.Tests.Models.Webhooks; + +public class VideoTransformationReadyEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new VideoTransformationReadyEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + Timings = new() { DownloadDuration = 0, EncodingDuration = 0 }, + }; + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + VideoTransformationReadyEventVideoTransformationReadyEventData expectedData = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }; + VideoTransformationReadyEventVideoTransformationReadyEventRequest expectedRequest = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + Timings expectedTimings = new() { DownloadDuration = 0, EncodingDuration = 0 }; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + Assert.Equal(expectedRequest, model.Request); + Assert.Equal(expectedTimings, model.Timings); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new VideoTransformationReadyEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + Timings = new() { DownloadDuration = 0, EncodingDuration = 0 }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new VideoTransformationReadyEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + Timings = new() { DownloadDuration = 0, EncodingDuration = 0 }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedID = "id"; + string expectedType = "type"; + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + VideoTransformationReadyEventVideoTransformationReadyEventData expectedData = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }; + VideoTransformationReadyEventVideoTransformationReadyEventRequest expectedRequest = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + Timings expectedTimings = new() { DownloadDuration = 0, EncodingDuration = 0 }; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + Assert.Equal(expectedRequest, deserialized.Request); + Assert.Equal(expectedTimings, deserialized.Timings); + } + + [Fact] + public void Validation_Works() + { + var model = new VideoTransformationReadyEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + Timings = new() { DownloadDuration = 0, EncodingDuration = 0 }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new VideoTransformationReadyEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + Assert.Null(model.Timings); + Assert.False(model.RawData.ContainsKey("timings")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new VideoTransformationReadyEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new VideoTransformationReadyEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + + // Null should be interpreted as omitted for these properties + Timings = null, + }; + + Assert.Null(model.Timings); + Assert.False(model.RawData.ContainsKey("timings")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new VideoTransformationReadyEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + + // Null should be interpreted as omitted for these properties + Timings = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new VideoTransformationReadyEvent + { + ID = "id", + Type = "type", + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + Timings = new() { DownloadDuration = 0, EncodingDuration = 0 }, + }; + + VideoTransformationReadyEvent copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class VideoTransformationReadyEventVideoTransformationReadyEventTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + Timings = new() { DownloadDuration = 0, EncodingDuration = 0 }, + }; + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + VideoTransformationReadyEventVideoTransformationReadyEventData expectedData = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }; + VideoTransformationReadyEventVideoTransformationReadyEventRequest expectedRequest = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("video.transformation.ready"); + Timings expectedTimings = new() { DownloadDuration = 0, EncodingDuration = 0 }; + + Assert.Equal(expectedCreatedAt, model.CreatedAt); + Assert.Equal(expectedData, model.Data); + Assert.Equal(expectedRequest, model.Request); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedTimings, model.Timings); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + Timings = new() { DownloadDuration = 0, EncodingDuration = 0 }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + Timings = new() { DownloadDuration = 0, EncodingDuration = 0 }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + DateTimeOffset expectedCreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); + VideoTransformationReadyEventVideoTransformationReadyEventData expectedData = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }; + VideoTransformationReadyEventVideoTransformationReadyEventRequest expectedRequest = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("video.transformation.ready"); + Timings expectedTimings = new() { DownloadDuration = 0, EncodingDuration = 0 }; + + Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); + Assert.Equal(expectedData, deserialized.Data); + Assert.Equal(expectedRequest, deserialized.Request); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedTimings, deserialized.Timings); + } + + [Fact] + public void Validation_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + Timings = new() { DownloadDuration = 0, EncodingDuration = 0 }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + Assert.Null(model.Timings); + Assert.False(model.RawData.ContainsKey("timings")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + + // Null should be interpreted as omitted for these properties + Timings = null, + }; + + Assert.Null(model.Timings); + Assert.False(model.RawData.ContainsKey("timings")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + + // Null should be interpreted as omitted for these properties + Timings = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEvent + { + CreatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Data = new() + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }, + Request = new() + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }, + Timings = new() { DownloadDuration = 0, EncodingDuration = 0 }, + }; + + VideoTransformationReadyEventVideoTransformationReadyEvent copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class VideoTransformationReadyEventVideoTransformationReadyEventDataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventData + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }; + + VideoTransformationReadyEventVideoTransformationReadyEventDataAsset expectedAsset = new( + "https://example.com" + ); + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation expectedTransformation = + new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }; + + Assert.Equal(expectedAsset, model.Asset); + Assert.Equal(expectedTransformation, model.Transformation); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventData + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventData + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + VideoTransformationReadyEventVideoTransformationReadyEventDataAsset expectedAsset = new( + "https://example.com" + ); + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation expectedTransformation = + new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }; + + Assert.Equal(expectedAsset, deserialized.Asset); + Assert.Equal(expectedTransformation, deserialized.Transformation); + } + + [Fact] + public void Validation_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventData + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventData + { + Asset = new("https://example.com"), + Transformation = new() + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }, + }; + + VideoTransformationReadyEventVideoTransformationReadyEventData copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class VideoTransformationReadyEventVideoTransformationReadyEventDataAssetTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventDataAsset + { + Url = "https://example.com", + }; + + string expectedUrl = "https://example.com"; + + Assert.Equal(expectedUrl, model.Url); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventDataAsset + { + Url = "https://example.com", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventDataAsset + { + Url = "https://example.com", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedUrl = "https://example.com"; + + Assert.Equal(expectedUrl, deserialized.Url); + } + + [Fact] + public void Validation_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventDataAsset + { + Url = "https://example.com", + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventDataAsset + { + Url = "https://example.com", + }; + + VideoTransformationReadyEventVideoTransformationReadyEventDataAsset copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationTest + : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }; + + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType + > expectedType = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation; + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions expectedOptions = + new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }; + Output expectedOutput = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }; + + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedOptions, model.Options); + Assert.Equal(expectedOutput, model.Output); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType + > expectedType = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation; + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions expectedOptions = + new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }; + Output expectedOutput = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }; + + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedOptions, deserialized.Options); + Assert.Equal(expectedOutput, deserialized.Output); + } + + [Fact] + public void Validation_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + }; + + Assert.Null(model.Options); + Assert.False(model.RawData.ContainsKey("options")); + Assert.Null(model.Output); + Assert.False(model.RawData.ContainsKey("output")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + + // Null should be interpreted as omitted for these properties + Options = null, + Output = null, + }; + + Assert.Null(model.Options); + Assert.False(model.RawData.ContainsKey("options")); + Assert.Null(model.Output); + Assert.False(model.RawData.ContainsKey("output")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + + // Null should be interpreted as omitted for these properties + Options = null, + Output = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation + { + Type = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + Options = new() + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }, + Output = new() + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }, + }; + + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation copied = new( + model + ); + + Assert.Equal(model, copied); + } +} + +public class VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationTypeTest + : TestBase +{ + [Theory] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation + )] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.GifToVideo + )] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoThumbnail + )] + public void Validation_Works( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType + > value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation + )] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.GifToVideo + )] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoThumbnail + )] + public void SerializationRoundtrip_Works( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType + > value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsTest + : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = + new VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }; + + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec + > expectedAudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac; + bool expectedAutoRotate = true; + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat + > expectedFormat = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4; + long expectedQuality = 0; + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol + > expectedStreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls; + List expectedVariants = ["string"]; + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec + > expectedVideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264; + + Assert.Equal(expectedAudioCodec, model.AudioCodec); + Assert.Equal(expectedAutoRotate, model.AutoRotate); + Assert.Equal(expectedFormat, model.Format); + Assert.Equal(expectedQuality, model.Quality); + Assert.Equal(expectedStreamProtocol, model.StreamProtocol); + Assert.NotNull(model.Variants); + Assert.Equal(expectedVariants.Count, model.Variants.Count); + for (int i = 0; i < expectedVariants.Count; i++) + { + Assert.Equal(expectedVariants[i], model.Variants[i]); + } + Assert.Equal(expectedVideoCodec, model.VideoCodec); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = + new VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = + new VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec + > expectedAudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac; + bool expectedAutoRotate = true; + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat + > expectedFormat = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4; + long expectedQuality = 0; + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol + > expectedStreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls; + List expectedVariants = ["string"]; + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec + > expectedVideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264; + + Assert.Equal(expectedAudioCodec, deserialized.AudioCodec); + Assert.Equal(expectedAutoRotate, deserialized.AutoRotate); + Assert.Equal(expectedFormat, deserialized.Format); + Assert.Equal(expectedQuality, deserialized.Quality); + Assert.Equal(expectedStreamProtocol, deserialized.StreamProtocol); + Assert.NotNull(deserialized.Variants); + Assert.Equal(expectedVariants.Count, deserialized.Variants.Count); + for (int i = 0; i < expectedVariants.Count; i++) + { + Assert.Equal(expectedVariants[i], deserialized.Variants[i]); + } + Assert.Equal(expectedVideoCodec, deserialized.VideoCodec); + } + + [Fact] + public void Validation_Works() + { + var model = + new VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = + new VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions + { }; + + Assert.Null(model.AudioCodec); + Assert.False(model.RawData.ContainsKey("audio_codec")); + Assert.Null(model.AutoRotate); + Assert.False(model.RawData.ContainsKey("auto_rotate")); + Assert.Null(model.Format); + Assert.False(model.RawData.ContainsKey("format")); + Assert.Null(model.Quality); + Assert.False(model.RawData.ContainsKey("quality")); + Assert.Null(model.StreamProtocol); + Assert.False(model.RawData.ContainsKey("stream_protocol")); + Assert.Null(model.Variants); + Assert.False(model.RawData.ContainsKey("variants")); + Assert.Null(model.VideoCodec); + Assert.False(model.RawData.ContainsKey("video_codec")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = + new VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions + { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = + new VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions + { + // Null should be interpreted as omitted for these properties + AudioCodec = null, + AutoRotate = null, + Format = null, + Quality = null, + StreamProtocol = null, + Variants = null, + VideoCodec = null, + }; + + Assert.Null(model.AudioCodec); + Assert.False(model.RawData.ContainsKey("audio_codec")); + Assert.Null(model.AutoRotate); + Assert.False(model.RawData.ContainsKey("auto_rotate")); + Assert.Null(model.Format); + Assert.False(model.RawData.ContainsKey("format")); + Assert.Null(model.Quality); + Assert.False(model.RawData.ContainsKey("quality")); + Assert.Null(model.StreamProtocol); + Assert.False(model.RawData.ContainsKey("stream_protocol")); + Assert.Null(model.Variants); + Assert.False(model.RawData.ContainsKey("variants")); + Assert.Null(model.VideoCodec); + Assert.False(model.RawData.ContainsKey("video_codec")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = + new VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions + { + // Null should be interpreted as omitted for these properties + AudioCodec = null, + AutoRotate = null, + Format = null, + Quality = null, + StreamProtocol = null, + Variants = null, + VideoCodec = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = + new VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions + { + AudioCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + AutoRotate = true, + Format = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + Quality = 0, + StreamProtocol = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + Variants = ["string"], + VideoCodec = + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + }; + + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions copied = + new(model); + + Assert.Equal(model, copied); + } +} + +public class VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodecTest + : TestBase +{ + [Theory] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac + )] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Opus + )] + public void Validation_Works( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec + > value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac + )] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Opus + )] + public void SerializationRoundtrip_Works( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec + > value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormatTest + : TestBase +{ + [Theory] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4 + )] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Webm + )] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Jpg + )] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Png + )] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Webp + )] + public void Validation_Works( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat + > value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4 + )] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Webm + )] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Jpg + )] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Png + )] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Webp + )] + public void SerializationRoundtrip_Works( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat + > value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocolTest + : TestBase +{ + [Theory] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls + )] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Dash + )] + public void Validation_Works( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol + > value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls + )] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Dash + )] + public void SerializationRoundtrip_Works( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol + > value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodecTest + : TestBase +{ + [Theory] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264 + )] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.Vp9 + )] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.Av1 + )] + public void Validation_Works( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec + > value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264 + )] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.Vp9 + )] + [InlineData( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.Av1 + )] + public void SerializationRoundtrip_Works( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec + > value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class OutputTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Output + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }; + + string expectedUrl = "https://example.com"; + VideoMetadata expectedVideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }; + + Assert.Equal(expectedUrl, model.Url); + Assert.Equal(expectedVideoMetadata, model.VideoMetadata); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Output + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Output + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + Assert.NotNull(deserialized); + + string expectedUrl = "https://example.com"; + VideoMetadata expectedVideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }; + + Assert.Equal(expectedUrl, deserialized.Url); + Assert.Equal(expectedVideoMetadata, deserialized.VideoMetadata); + } + + [Fact] + public void Validation_Works() + { + var model = new Output + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Output { Url = "https://example.com" }; + + Assert.Null(model.VideoMetadata); + Assert.False(model.RawData.ContainsKey("video_metadata")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Output { Url = "https://example.com" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Output + { + Url = "https://example.com", + + // Null should be interpreted as omitted for these properties + VideoMetadata = null, + }; + + Assert.Null(model.VideoMetadata); + Assert.False(model.RawData.ContainsKey("video_metadata")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Output + { + Url = "https://example.com", + + // Null should be interpreted as omitted for these properties + VideoMetadata = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Output + { + Url = "https://example.com", + VideoMetadata = new() + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }, + }; + + Output copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class VideoMetadataTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new VideoMetadata + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }; + + long expectedBitrate = 0; + double expectedDuration = 0; + long expectedHeight = 0; + long expectedWidth = 0; + + Assert.Equal(expectedBitrate, model.Bitrate); + Assert.Equal(expectedDuration, model.Duration); + Assert.Equal(expectedHeight, model.Height); + Assert.Equal(expectedWidth, model.Width); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new VideoMetadata + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new VideoMetadata + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + long expectedBitrate = 0; + double expectedDuration = 0; + long expectedHeight = 0; + long expectedWidth = 0; + + Assert.Equal(expectedBitrate, deserialized.Bitrate); + Assert.Equal(expectedDuration, deserialized.Duration); + Assert.Equal(expectedHeight, deserialized.Height); + Assert.Equal(expectedWidth, deserialized.Width); + } + + [Fact] + public void Validation_Works() + { + var model = new VideoMetadata + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new VideoMetadata + { + Bitrate = 0, + Duration = 0, + Height = 0, + Width = 0, + }; + + VideoMetadata copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class VideoTransformationReadyEventVideoTransformationReadyEventRequestTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + + string expectedUrl = "https://example.com"; + string expectedXRequestID = "x_request_id"; + string expectedUserAgent = "user_agent"; + + Assert.Equal(expectedUrl, model.Url); + Assert.Equal(expectedXRequestID, model.XRequestID); + Assert.Equal(expectedUserAgent, model.UserAgent); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedUrl = "https://example.com"; + string expectedXRequestID = "x_request_id"; + string expectedUserAgent = "user_agent"; + + Assert.Equal(expectedUrl, deserialized.Url); + Assert.Equal(expectedXRequestID, deserialized.XRequestID); + Assert.Equal(expectedUserAgent, deserialized.UserAgent); + } + + [Fact] + public void Validation_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + }; + + Assert.Null(model.UserAgent); + Assert.False(model.RawData.ContainsKey("user_agent")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + + // Null should be interpreted as omitted for these properties + UserAgent = null, + }; + + Assert.Null(model.UserAgent); + Assert.False(model.RawData.ContainsKey("user_agent")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + + // Null should be interpreted as omitted for these properties + UserAgent = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new VideoTransformationReadyEventVideoTransformationReadyEventRequest + { + Url = "https://example.com", + XRequestID = "x_request_id", + UserAgent = "user_agent", + }; + + VideoTransformationReadyEventVideoTransformationReadyEventRequest copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class TimingsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Timings { DownloadDuration = 0, EncodingDuration = 0 }; + + long expectedDownloadDuration = 0; + long expectedEncodingDuration = 0; + + Assert.Equal(expectedDownloadDuration, model.DownloadDuration); + Assert.Equal(expectedEncodingDuration, model.EncodingDuration); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Timings { DownloadDuration = 0, EncodingDuration = 0 }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(json, ModelBase.SerializerOptions); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Timings { DownloadDuration = 0, EncodingDuration = 0 }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + long expectedDownloadDuration = 0; + long expectedEncodingDuration = 0; + + Assert.Equal(expectedDownloadDuration, deserialized.DownloadDuration); + Assert.Equal(expectedEncodingDuration, deserialized.EncodingDuration); + } + + [Fact] + public void Validation_Works() + { + var model = new Timings { DownloadDuration = 0, EncodingDuration = 0 }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Timings { }; + + Assert.Null(model.DownloadDuration); + Assert.False(model.RawData.ContainsKey("download_duration")); + Assert.Null(model.EncodingDuration); + Assert.False(model.RawData.ContainsKey("encoding_duration")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Timings { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Timings + { + // Null should be interpreted as omitted for these properties + DownloadDuration = null, + EncodingDuration = null, + }; + + Assert.Null(model.DownloadDuration); + Assert.False(model.RawData.ContainsKey("download_duration")); + Assert.Null(model.EncodingDuration); + Assert.False(model.RawData.ContainsKey("encoding_duration")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Timings + { + // Null should be interpreted as omitted for these properties + DownloadDuration = null, + EncodingDuration = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Timings { DownloadDuration = 0, EncodingDuration = 0 }; + + Timings copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Imagekit.Tests/RetriesTest.cs b/src/Imagekit.Tests/RetriesTest.cs new file mode 100644 index 00000000..c60b4e7f --- /dev/null +++ b/src/Imagekit.Tests/RetriesTest.cs @@ -0,0 +1,359 @@ +using System; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Imagekit; +using Imagekit.Core; +using Moq; +using Moq.Protected; + +namespace Imagekit.Tests; + +public class RetriesTest : TestBase +{ + record class BlankParams : ParamsBase + { + internal override void AddHeadersToRequest( + HttpRequestMessage _request, + ClientOptions _options + ) + { + // do nothing + } + + public override Uri Url(ClientOptions _options) + { + return new Uri("http://localhost/something"); + } + } + + record class ParamsWithOverwrittenRetryHeader : ParamsBase + { + internal override void AddHeadersToRequest( + HttpRequestMessage request, + ClientOptions _options + ) + { + request.Headers.TryAddWithoutValidation("x-stainless-retry-count", "42"); + } + + public override Uri Url(ClientOptions _options) + { + return new Uri("http://localhost/something"); + } + } + + [Fact] + public async Task ImmediateSuccess_Works() + { + var handlerMock = new Mock(); + handlerMock + .Protected() + .Setup>( + "SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny() + ) + .ReturnsAsync( + new HttpResponseMessage() + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent("foo"), + } + ); + + var httpClient = new HttpClient(handlerMock.Object); + + ImageKitClient client = new() { HttpClient = httpClient, MaxRetries = 2 }; + + var resp = await client.WithRawResponse.Execute( + new HttpRequest { Method = HttpMethod.Get, Params = new() }, + TestContext.Current.CancellationToken + ); + + Assert.Equal(HttpStatusCode.OK, resp.StatusCode); + + handlerMock + .Protected() + .Verify( + "SendAsync", + Times.Exactly(1), + ItExpr.Is( + (req) => + req.Method == HttpMethod.Get + && req.RequestUri == new Uri("http://localhost/something") + ), + ItExpr.IsAny() + ); + } + + [Fact] + public async Task RetryAfterHeader_Works() + { + var ResponseWithRetryDate = new HttpResponseMessage() + { + StatusCode = HttpStatusCode.ServiceUnavailable, + Content = new StringContent("foo"), + }; + ResponseWithRetryDate.Headers.Add("Retry-After", "Wed, 21 Oct 2015 07:28:00 GMT"); + + var ResponseWithRetryDelay = new HttpResponseMessage() + { + StatusCode = HttpStatusCode.ServiceUnavailable, + Content = new StringContent("foo"), + }; + // decimals are technically out of spec, but we want to ensure we can parse them regardless + ResponseWithRetryDelay.Headers.TryAddWithoutValidation("Retry-After", "1.234"); + + var handlerMock = new Mock(); + handlerMock + .Protected() + .SetupSequence>( + "SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny() + ) + .ReturnsAsync(ResponseWithRetryDate) + .ReturnsAsync(ResponseWithRetryDelay) + .ReturnsAsync( + new HttpResponseMessage() + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent("foo"), + } + ); + + var httpClient = new HttpClient(handlerMock.Object); + + ImageKitClient client = new() { HttpClient = httpClient, MaxRetries = 2 }; + + var resp = await client.WithRawResponse.Execute( + new HttpRequest { Method = HttpMethod.Get, Params = new() }, + TestContext.Current.CancellationToken + ); + + Assert.Equal(HttpStatusCode.OK, resp.StatusCode); + handlerMock + .Protected() + .Verify( + "SendAsync", + Times.Exactly(1), + ItExpr.Is( + (req) => + req.Method == HttpMethod.Get + && req.RequestUri == new Uri("http://localhost/something") + && Enumerable.Single(req.Headers.GetValues("x-stainless-retry-count")) + == "0" + ), + ItExpr.IsAny() + ); + handlerMock + .Protected() + .Verify( + "SendAsync", + Times.Exactly(1), + ItExpr.Is( + (req) => + req.Method == HttpMethod.Get + && req.RequestUri == new Uri("http://localhost/something") + && Enumerable.Single(req.Headers.GetValues("x-stainless-retry-count")) + == "1" + ), + ItExpr.IsAny() + ); + handlerMock + .Protected() + .Verify( + "SendAsync", + Times.Exactly(1), + ItExpr.Is( + (req) => + req.Method == HttpMethod.Get + && req.RequestUri == new Uri("http://localhost/something") + && Enumerable.Single(req.Headers.GetValues("x-stainless-retry-count")) + == "2" + ), + ItExpr.IsAny() + ); + } + + [Fact] + public async Task OverwrittenRetryCountHeader_Works() + { + var handlerMock = new Mock(); + handlerMock + .Protected() + .SetupSequence>( + "SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny() + ) + .ReturnsAsync( + new HttpResponseMessage() + { + StatusCode = HttpStatusCode.ServiceUnavailable, + Content = new StringContent("foo"), + } + ) + .ReturnsAsync( + new HttpResponseMessage() + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent("foo"), + } + ); + + var httpClient = new HttpClient(handlerMock.Object); + + ImageKitClient client = new() { HttpClient = httpClient, MaxRetries = 2 }; + + var resp = await client.WithRawResponse.Execute( + new HttpRequest + { + Method = HttpMethod.Get, + Params = new(), + }, + TestContext.Current.CancellationToken + ); + + Assert.Equal(HttpStatusCode.OK, resp.StatusCode); + + handlerMock + .Protected() + .Verify( + "SendAsync", + Times.Exactly(2), + ItExpr.Is( + (req) => + req.Method == HttpMethod.Get + && req.RequestUri == new Uri("http://localhost/something") + && Enumerable.Single(req.Headers.GetValues("x-stainless-retry-count")) + == "42" + ), + ItExpr.IsAny() + ); + } + + [Fact] + public async Task RetryAfterMsHeader_Works() + { + var failResponse = new HttpResponseMessage() + { + StatusCode = HttpStatusCode.ServiceUnavailable, + Content = new StringContent("foo"), + }; + failResponse.Headers.TryAddWithoutValidation("Retry-After-Ms", "10"); + + var handlerMock = new Mock(); + handlerMock + .Protected() + .SetupSequence>( + "SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny() + ) + .ReturnsAsync(failResponse) + .ReturnsAsync( + new HttpResponseMessage() + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent("foo"), + } + ); + + var httpClient = new HttpClient(handlerMock.Object); + + ImageKitClient client = new() { HttpClient = httpClient, MaxRetries = 1 }; + + var resp = await client.WithRawResponse.Execute( + new HttpRequest { Method = HttpMethod.Get, Params = new() }, + TestContext.Current.CancellationToken + ); + + Assert.Equal(HttpStatusCode.OK, resp.StatusCode); + handlerMock + .Protected() + .Verify( + "SendAsync", + Times.Exactly(2), + ItExpr.Is( + (req) => + req.Method == HttpMethod.Get + && req.RequestUri == new Uri("http://localhost/something") + ), + ItExpr.IsAny() + ); + } + + [Fact] + public async Task RetryableException_Works() + { + var callCount = 0; + + var handlerMock = new Mock(); + handlerMock + .Protected() + .Setup>( + "SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny() + ) + .Returns( + (_, ct) => + { + callCount++; + if (callCount == 1) + throw new HttpRequestException("Simulated retryable failure"); + + return Task.FromResult( + new HttpResponseMessage() + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent("foo"), + } + ); + } + ); + + var httpClient = new HttpClient(handlerMock.Object); + + ImageKitClient client = new() { HttpClient = httpClient, MaxRetries = 2 }; + + var resp = await client.WithRawResponse.Execute( + new HttpRequest { Method = HttpMethod.Get, Params = new() }, + TestContext.Current.CancellationToken + ); + + Assert.Equal(HttpStatusCode.OK, resp.StatusCode); + handlerMock + .Protected() + .Verify( + "SendAsync", + Times.Exactly(1), + ItExpr.Is( + (req) => + req.Method == HttpMethod.Get + && req.RequestUri == new Uri("http://localhost/something") + && Enumerable.Single(req.Headers.GetValues("x-stainless-retry-count")) + == "0" + ), + ItExpr.IsAny() + ); + handlerMock + .Protected() + .Verify( + "SendAsync", + Times.Exactly(1), + ItExpr.Is( + (req) => + req.Method == HttpMethod.Get + && req.RequestUri == new Uri("http://localhost/something") + && Enumerable.Single(req.Headers.GetValues("x-stainless-retry-count")) + == "1" + ), + ItExpr.IsAny() + ); + } +} diff --git a/src/Imagekit.Tests/Services/Accounts/OriginServiceTest.cs b/src/Imagekit.Tests/Services/Accounts/OriginServiceTest.cs new file mode 100644 index 00000000..c8a31cff --- /dev/null +++ b/src/Imagekit.Tests/Services/Accounts/OriginServiceTest.cs @@ -0,0 +1,86 @@ +using System.Threading.Tasks; +using Imagekit.Models.Accounts.Origins; + +namespace Imagekit.Tests.Services.Accounts; + +public class OriginServiceTest : TestBase +{ + [Fact(Skip = "Mock server tests are disabled")] + public async Task Create_Works() + { + var originResponse = await this.client.Accounts.Origins.Create( + new() + { + OriginRequest = new S3() + { + AccessKey = "AKIATEST123", + Bucket = "test-bucket", + Name = "My S3 Origin", + SecretKey = "secrettest123", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "images", + }, + }, + TestContext.Current.CancellationToken + ); + originResponse.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Update_Works() + { + var originResponse = await this.client.Accounts.Origins.Update( + "id", + new() + { + OriginRequest = new S3() + { + AccessKey = "AKIATEST123", + Bucket = "test-bucket", + Name = "My S3 Origin", + SecretKey = "secrettest123", + BaseUrlForCanonicalHeader = "https://cdn.example.com", + IncludeCanonicalHeader = false, + Prefix = "images", + }, + }, + TestContext.Current.CancellationToken + ); + originResponse.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task List_Works() + { + var originResponses = await this.client.Accounts.Origins.List( + new(), + TestContext.Current.CancellationToken + ); + foreach (var item in originResponses) + { + item.Validate(); + } + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Delete_Works() + { + await this.client.Accounts.Origins.Delete( + "id", + new(), + TestContext.Current.CancellationToken + ); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Get_Works() + { + var originResponse = await this.client.Accounts.Origins.Get( + "id", + new(), + TestContext.Current.CancellationToken + ); + originResponse.Validate(); + } +} diff --git a/src/Imagekit.Tests/Services/Accounts/UrlEndpointServiceTest.cs b/src/Imagekit.Tests/Services/Accounts/UrlEndpointServiceTest.cs new file mode 100644 index 00000000..0b53ee12 --- /dev/null +++ b/src/Imagekit.Tests/Services/Accounts/UrlEndpointServiceTest.cs @@ -0,0 +1,61 @@ +using System.Threading.Tasks; + +namespace Imagekit.Tests.Services.Accounts; + +public class UrlEndpointServiceTest : TestBase +{ + [Fact(Skip = "Mock server tests are disabled")] + public async Task Create_Works() + { + var urlEndpointResponse = await this.client.Accounts.UrlEndpoints.Create( + new() { Description = "My custom URL endpoint" }, + TestContext.Current.CancellationToken + ); + urlEndpointResponse.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Update_Works() + { + var urlEndpointResponse = await this.client.Accounts.UrlEndpoints.Update( + "id", + new() { Description = "My custom URL endpoint" }, + TestContext.Current.CancellationToken + ); + urlEndpointResponse.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task List_Works() + { + var urlEndpointResponses = await this.client.Accounts.UrlEndpoints.List( + new(), + TestContext.Current.CancellationToken + ); + foreach (var item in urlEndpointResponses) + { + item.Validate(); + } + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Delete_Works() + { + await this.client.Accounts.UrlEndpoints.Delete( + "id", + new(), + TestContext.Current.CancellationToken + ); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Get_Works() + { + var urlEndpointResponse = await this.client.Accounts.UrlEndpoints.Get( + "id", + new(), + TestContext.Current.CancellationToken + ); + urlEndpointResponse.Validate(); + } +} diff --git a/src/Imagekit.Tests/Services/Accounts/UsageServiceTest.cs b/src/Imagekit.Tests/Services/Accounts/UsageServiceTest.cs new file mode 100644 index 00000000..ae97a44c --- /dev/null +++ b/src/Imagekit.Tests/Services/Accounts/UsageServiceTest.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; + +namespace Imagekit.Tests.Services.Accounts; + +public class UsageServiceTest : TestBase +{ + [Fact(Skip = "Mock server tests are disabled")] + public async Task Get_Works() + { + var usage = await this.client.Accounts.Usage.Get( + new() { EndDate = "2019-12-27", StartDate = "2019-12-27" }, + TestContext.Current.CancellationToken + ); + usage.Validate(); + } +} diff --git a/src/Imagekit.Tests/Services/AssetServiceTest.cs b/src/Imagekit.Tests/Services/AssetServiceTest.cs new file mode 100644 index 00000000..8e9e585f --- /dev/null +++ b/src/Imagekit.Tests/Services/AssetServiceTest.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; + +namespace Imagekit.Tests.Services; + +public class AssetServiceTest : TestBase +{ + [Fact(Skip = "Mock server tests are disabled")] + public async Task List_Works() + { + var assets = await this.client.Assets.List(new(), TestContext.Current.CancellationToken); + foreach (var item in assets) + { + item.Validate(); + } + } +} diff --git a/src/Imagekit.Tests/Services/Beta/V2/FileServiceTest.cs b/src/Imagekit.Tests/Services/Beta/V2/FileServiceTest.cs new file mode 100644 index 00000000..41119a76 --- /dev/null +++ b/src/Imagekit.Tests/Services/Beta/V2/FileServiceTest.cs @@ -0,0 +1,17 @@ +using System.Text; +using System.Threading.Tasks; + +namespace Imagekit.Tests.Services.Beta.V2; + +public class FileServiceTest : TestBase +{ + [Fact(Skip = "Mock server tests are disabled")] + public async Task Upload_Works() + { + var response = await this.client.Beta.V2.Files.Upload( + new() { File = Encoding.UTF8.GetBytes("Example data"), FileName = "fileName" }, + TestContext.Current.CancellationToken + ); + response.Validate(); + } +} diff --git a/src/Imagekit.Tests/Services/Cache/InvalidationServiceTest.cs b/src/Imagekit.Tests/Services/Cache/InvalidationServiceTest.cs new file mode 100644 index 00000000..30233526 --- /dev/null +++ b/src/Imagekit.Tests/Services/Cache/InvalidationServiceTest.cs @@ -0,0 +1,27 @@ +using System.Threading.Tasks; + +namespace Imagekit.Tests.Services.Cache; + +public class InvalidationServiceTest : TestBase +{ + [Fact(Skip = "Mock server tests are disabled")] + public async Task Create_Works() + { + var invalidation = await this.client.Cache.Invalidation.Create( + new() { UrlValue = "https://ik.imagekit.io/your_imagekit_id/default-image.jpg" }, + TestContext.Current.CancellationToken + ); + invalidation.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Get_Works() + { + var invalidation = await this.client.Cache.Invalidation.Get( + "requestId", + new(), + TestContext.Current.CancellationToken + ); + invalidation.Validate(); + } +} diff --git a/src/Imagekit.Tests/Services/CustomMetadataFieldServiceTest.cs b/src/Imagekit.Tests/Services/CustomMetadataFieldServiceTest.cs new file mode 100644 index 00000000..d9e95236 --- /dev/null +++ b/src/Imagekit.Tests/Services/CustomMetadataFieldServiceTest.cs @@ -0,0 +1,73 @@ +using System.Threading.Tasks; +using Imagekit.Models.CustomMetadataFields; + +namespace Imagekit.Tests.Services; + +public class CustomMetadataFieldServiceTest : TestBase +{ + [Fact(Skip = "Mock server tests are disabled")] + public async Task Create_Works() + { + var customMetadataField = await this.client.CustomMetadataFields.Create( + new() + { + Label = "price", + Name = "price", + Schema = new() + { + Type = Type.Number, + DefaultValue = new( + [ + new DefaultValueItem(true), + new DefaultValueItem(10), + new DefaultValueItem("Hello"), + ] + ), + IsValueRequired = true, + MaxLength = 0, + MaxValue = 3000, + MinLength = 0, + MinValue = 1000, + SelectOptions = ["small", "medium", "large", 30, 40, true], + }, + }, + TestContext.Current.CancellationToken + ); + customMetadataField.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Update_Works() + { + var customMetadataField = await this.client.CustomMetadataFields.Update( + "id", + new(), + TestContext.Current.CancellationToken + ); + customMetadataField.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task List_Works() + { + var customMetadataFields = await this.client.CustomMetadataFields.List( + new(), + TestContext.Current.CancellationToken + ); + foreach (var item in customMetadataFields) + { + item.Validate(); + } + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Delete_Works() + { + var customMetadataField = await this.client.CustomMetadataFields.Delete( + "id", + new(), + TestContext.Current.CancellationToken + ); + customMetadataField.Validate(); + } +} diff --git a/src/Imagekit.Tests/Services/DummyServiceTest.cs b/src/Imagekit.Tests/Services/DummyServiceTest.cs new file mode 100644 index 00000000..135026e8 --- /dev/null +++ b/src/Imagekit.Tests/Services/DummyServiceTest.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; + +namespace Imagekit.Tests.Services; + +public class DummyServiceTest : TestBase +{ + [Fact(Skip = "Mock server tests are disabled")] + public async Task Create_Works() + { + await this.client.Dummy.Create(new(), TestContext.Current.CancellationToken); + } +} diff --git a/src/Imagekit.Tests/Services/FileServiceTest.cs b/src/Imagekit.Tests/Services/FileServiceTest.cs new file mode 100644 index 00000000..066bb71e --- /dev/null +++ b/src/Imagekit.Tests/Services/FileServiceTest.cs @@ -0,0 +1,140 @@ +using System.Collections.Generic; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Imagekit.Models.Files; +using Models = Imagekit.Models; + +namespace Imagekit.Tests.Services; + +public class FileServiceTest : TestBase +{ + [Fact(Skip = "Mock server tests are disabled")] + public async Task Update_Works() + { + var file = await this.client.Files.Update( + "fileId", + new() + { + UpdateFileRequest = new UpdateFileDetails() + { + CustomCoordinates = "10,10,100,100", + CustomMetadata = new Dictionary() + { + { "brand", JsonSerializer.SerializeToElement("bar") }, + { "color", JsonSerializer.SerializeToElement("bar") }, + }, + Description = "description", + Extensions = + [ + new Models::ExtensionItemRemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + new Models::ExtensionItemAutoTaggingExtension() + { + MaxTags = 10, + MinConfidence = 80, + Name = Models::ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + }, + new Models::ExtensionItemAutoTaggingExtension() + { + MaxTags = 10, + MinConfidence = 80, + Name = Models::ExtensionItemAutoTaggingExtensionName.AwsAutoTagging, + }, + new Models::ExtensionItemAIAutoDescription(), + new Models::ExtensionItemAITasks( + [ + new Models::ExtensionItemAITasksTaskSelectTags() + { + Instruction = "What types of clothing items are visible?", + MaxSelections = 1, + MinSelections = 0, + Vocabulary = ["shirt", "dress", "jacket"], + }, + ] + ), + new Models::SavedExtension("ext_abc123"), + ], + RemoveAITags = new(["car", "vehicle", "motorsports"]), + Tags = ["tag1", "tag2"], + WebhookUrl = "https://webhook.site/0d6b6c7a-8e5a-4b3a-8b7c-0d6b6c7a8e5a", + }, + }, + TestContext.Current.CancellationToken + ); + file.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Delete_Works() + { + await this.client.Files.Delete("fileId", new(), TestContext.Current.CancellationToken); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Copy_Works() + { + var response = await this.client.Files.Copy( + new() + { + DestinationPath = "/folder/to/copy/into/", + SourceFilePath = "/path/to/file.jpg", + }, + TestContext.Current.CancellationToken + ); + response.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Get_Works() + { + var file = await this.client.Files.Get( + "fileId", + new(), + TestContext.Current.CancellationToken + ); + file.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Move_Works() + { + var response = await this.client.Files.Move( + new() + { + DestinationPath = "/folder/to/move/into/", + SourceFilePath = "/path/to/file.jpg", + }, + TestContext.Current.CancellationToken + ); + response.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Rename_Works() + { + var response = await this.client.Files.Rename( + new() { FilePath = "/path/to/file.jpg", NewFileName = "newFileName.jpg" }, + TestContext.Current.CancellationToken + ); + response.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Upload_Works() + { + var response = await this.client.Files.Upload( + new() { File = Encoding.UTF8.GetBytes("Example data"), FileName = "fileName" }, + TestContext.Current.CancellationToken + ); + response.Validate(); + } +} diff --git a/src/Imagekit.Tests/Services/Files/BulkServiceTest.cs b/src/Imagekit.Tests/Services/Files/BulkServiceTest.cs new file mode 100644 index 00000000..4cf50d5c --- /dev/null +++ b/src/Imagekit.Tests/Services/Files/BulkServiceTest.cs @@ -0,0 +1,58 @@ +using System.Threading.Tasks; + +namespace Imagekit.Tests.Services.Files; + +public class BulkServiceTest : TestBase +{ + [Fact(Skip = "Mock server tests are disabled")] + public async Task Delete_Works() + { + var bulk = await this.client.Files.Bulk.Delete( + new() { FileIds = ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"] }, + TestContext.Current.CancellationToken + ); + bulk.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task AddTags_Works() + { + var response = await this.client.Files.Bulk.AddTags( + new() + { + FileIds = ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + Tags = ["t-shirt", "round-neck", "sale2019"], + }, + TestContext.Current.CancellationToken + ); + response.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task RemoveAITags_Works() + { + var response = await this.client.Files.Bulk.RemoveAITags( + new() + { + AITags = ["t-shirt", "round-neck", "sale2019"], + FileIds = ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + }, + TestContext.Current.CancellationToken + ); + response.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task RemoveTags_Works() + { + var response = await this.client.Files.Bulk.RemoveTags( + new() + { + FileIds = ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + Tags = ["t-shirt", "round-neck", "sale2019"], + }, + TestContext.Current.CancellationToken + ); + response.Validate(); + } +} diff --git a/src/Imagekit.Tests/Services/Files/MetadataServiceTest.cs b/src/Imagekit.Tests/Services/Files/MetadataServiceTest.cs new file mode 100644 index 00000000..b38204db --- /dev/null +++ b/src/Imagekit.Tests/Services/Files/MetadataServiceTest.cs @@ -0,0 +1,27 @@ +using System.Threading.Tasks; + +namespace Imagekit.Tests.Services.Files; + +public class MetadataServiceTest : TestBase +{ + [Fact(Skip = "Mock server tests are disabled")] + public async Task Get_Works() + { + var metadata = await this.client.Files.Metadata.Get( + "fileId", + new(), + TestContext.Current.CancellationToken + ); + metadata.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task GetFromUrl_Works() + { + var metadata = await this.client.Files.Metadata.GetFromUrl( + new() { UrlValue = "https://example.com" }, + TestContext.Current.CancellationToken + ); + metadata.Validate(); + } +} diff --git a/src/Imagekit.Tests/Services/Files/VersionServiceTest.cs b/src/Imagekit.Tests/Services/Files/VersionServiceTest.cs new file mode 100644 index 00000000..e3594102 --- /dev/null +++ b/src/Imagekit.Tests/Services/Files/VersionServiceTest.cs @@ -0,0 +1,53 @@ +using System.Threading.Tasks; + +namespace Imagekit.Tests.Services.Files; + +public class VersionServiceTest : TestBase +{ + [Fact(Skip = "Mock server tests are disabled")] + public async Task List_Works() + { + var files = await this.client.Files.Versions.List( + "fileId", + new(), + TestContext.Current.CancellationToken + ); + foreach (var item in files) + { + item.Validate(); + } + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Delete_Works() + { + var version = await this.client.Files.Versions.Delete( + "versionId", + new() { FileID = "fileId" }, + TestContext.Current.CancellationToken + ); + version.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Get_Works() + { + var file = await this.client.Files.Versions.Get( + "versionId", + new() { FileID = "fileId" }, + TestContext.Current.CancellationToken + ); + file.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Restore_Works() + { + var file = await this.client.Files.Versions.Restore( + "versionId", + new() { FileID = "fileId" }, + TestContext.Current.CancellationToken + ); + file.Validate(); + } +} diff --git a/src/Imagekit.Tests/Services/FolderServiceTest.cs b/src/Imagekit.Tests/Services/FolderServiceTest.cs new file mode 100644 index 00000000..f2a351f4 --- /dev/null +++ b/src/Imagekit.Tests/Services/FolderServiceTest.cs @@ -0,0 +1,64 @@ +using System.Threading.Tasks; + +namespace Imagekit.Tests.Services; + +public class FolderServiceTest : TestBase +{ + [Fact(Skip = "Mock server tests are disabled")] + public async Task Create_Works() + { + var folder = await this.client.Folders.Create( + new() { FolderName = "summer", ParentFolderPath = "/product/images/" }, + TestContext.Current.CancellationToken + ); + folder.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Delete_Works() + { + var folder = await this.client.Folders.Delete( + new() { FolderPath = "/folder/to/delete/" }, + TestContext.Current.CancellationToken + ); + folder.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Copy_Works() + { + var response = await this.client.Folders.Copy( + new() + { + DestinationPath = "/path/of/destination/folder", + SourceFolderPath = "/path/of/source/folder", + }, + TestContext.Current.CancellationToken + ); + response.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Move_Works() + { + var response = await this.client.Folders.Move( + new() + { + DestinationPath = "/path/of/destination/folder", + SourceFolderPath = "/path/of/source/folder", + }, + TestContext.Current.CancellationToken + ); + response.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Rename_Works() + { + var response = await this.client.Folders.Rename( + new() { FolderPath = "/path/of/folder", NewFolderName = "new-folder-name" }, + TestContext.Current.CancellationToken + ); + response.Validate(); + } +} diff --git a/src/Imagekit.Tests/Services/Folders/JobServiceTest.cs b/src/Imagekit.Tests/Services/Folders/JobServiceTest.cs new file mode 100644 index 00000000..2864fabf --- /dev/null +++ b/src/Imagekit.Tests/Services/Folders/JobServiceTest.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; + +namespace Imagekit.Tests.Services.Folders; + +public class JobServiceTest : TestBase +{ + [Fact(Skip = "Mock server tests are disabled")] + public async Task Get_Works() + { + var job = await this.client.Folders.Job.Get( + "jobId", + new(), + TestContext.Current.CancellationToken + ); + job.Validate(); + } +} diff --git a/src/Imagekit.Tests/Services/SavedExtensionServiceTest.cs b/src/Imagekit.Tests/Services/SavedExtensionServiceTest.cs new file mode 100644 index 00000000..150bf54d --- /dev/null +++ b/src/Imagekit.Tests/Services/SavedExtensionServiceTest.cs @@ -0,0 +1,76 @@ +using System.Threading.Tasks; +using Models = Imagekit.Models; + +namespace Imagekit.Tests.Services; + +public class SavedExtensionServiceTest : TestBase +{ + [Fact(Skip = "Mock server tests are disabled")] + public async Task Create_Works() + { + var savedExtension = await this.client.SavedExtensions.Create( + new() + { + Config = new Models::RemoveBg() + { + Options = new() + { + AddShadow = true, + BgColor = "bg_color", + BgImageUrl = "bg_image_url", + Semitransparency = true, + }, + }, + Description = "Analyzes vehicle images for type, condition, and quality assessment", + Name = "Car Quality Analysis", + }, + TestContext.Current.CancellationToken + ); + savedExtension.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Update_Works() + { + var savedExtension = await this.client.SavedExtensions.Update( + "id", + new(), + TestContext.Current.CancellationToken + ); + savedExtension.Validate(); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task List_Works() + { + var savedExtensions = await this.client.SavedExtensions.List( + new(), + TestContext.Current.CancellationToken + ); + foreach (var item in savedExtensions) + { + item.Validate(); + } + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Delete_Works() + { + await this.client.SavedExtensions.Delete( + "id", + new(), + TestContext.Current.CancellationToken + ); + } + + [Fact(Skip = "Mock server tests are disabled")] + public async Task Get_Works() + { + var savedExtension = await this.client.SavedExtensions.Get( + "id", + new(), + TestContext.Current.CancellationToken + ); + savedExtension.Validate(); + } +} diff --git a/src/Imagekit.Tests/TestBase.cs b/src/Imagekit.Tests/TestBase.cs new file mode 100644 index 00000000..e661d50f --- /dev/null +++ b/src/Imagekit.Tests/TestBase.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Imagekit; + +namespace Imagekit.Tests; + +public class TestBase +{ + protected IImageKitClient client; + + public TestBase() + { + client = new ImageKitClient() + { + BaseUrl = + Environment.GetEnvironmentVariable("TEST_API_BASE_URL") ?? "http://localhost:4010", + PrivateKey = "My Private Key", + Password = "My Password", + }; + } + + internal static bool UrisEqual(Uri uri1, Uri uri2) + { + if ( + uri1.Scheme != uri2.Scheme + || uri1.Host != uri2.Host + || uri1.Port != uri2.Port + || uri1.AbsolutePath != uri2.AbsolutePath + ) + { + return false; + } + + var query1 = ParseQueryString(uri1.Query); + var query2 = ParseQueryString(uri2.Query); + + return Enumerable.SequenceEqual(query1, query2); + } + + static SortedDictionary ParseQueryString(string query) + { + var ret = new SortedDictionary(StringComparer.Ordinal); + + if (string.IsNullOrEmpty(query)) + return ret; + + var pairs = query.TrimStart('?').Split(['&'], StringSplitOptions.RemoveEmptyEntries); + + foreach (var pair in pairs) + { + var parts = pair.Split(['&'], 2); + var key = Uri.UnescapeDataString(parts[0]); + var value = parts.Length > 1 ? Uri.UnescapeDataString(parts[1]) : string.Empty; + ret[key] = value; + } + + return ret; + } +} diff --git a/src/Imagekit/Core/ApiEnum.cs b/src/Imagekit/Core/ApiEnum.cs new file mode 100644 index 00000000..a3e25efe --- /dev/null +++ b/src/Imagekit/Core/ApiEnum.cs @@ -0,0 +1,146 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Exceptions; + +namespace Imagekit.Core; + +/// +/// A serializable and deserializable enum wrapper type that handles the possibility of values outside the +/// range of known enum members. +/// +/// In most cases you don't have to worry about this type and can rely on its implicit operators to +/// wrap and unwrap enum values. +/// +public record class ApiEnum + where TEnum : struct, Enum +{ + /// + /// Returns this instance's raw value. + /// + /// This is usually only useful if this instance was deserialized from data that doesn't match the + /// expected type (), and you want to know that value. For example, if the + /// SDK is on an older version than the API, then the API may respond with new data types that the SDK is + /// unaware of. + /// + /// + public JsonElement Json; + + public ApiEnum(JsonElement json) + { + Json = json; + } + + /// + /// Returns this instance's raw value. + /// + /// This is usually only useful if this instance was deserialized from data that doesn't match + /// any known enum member, and you want to know that value. For example, if the SDK is on an older + /// version than the API, then the API may respond with new members that the SDK is unaware of. + /// + /// + /// Thrown when this instance's raw value isn't of type . Use + /// to access the raw value. + /// + /// + public TRaw Raw() + { + try + { + return JsonSerializer.Deserialize(this.Json, ModelBase.SerializerOptions) + ?? throw new ImageKitInvalidDataException($"{nameof(this.Json)} cannot be null"); + } + catch (JsonException e) + { + throw new ImageKitInvalidDataException( + $"{this.Json} must be of type {typeof(TRaw).FullName}", + e + ); + } + } + + /// + /// Returns an enum member corresponding to this instance's value, or (TEnum)(-1) if the + /// class was instantiated with an unknown value. + /// + /// Use to access the raw value.. + /// + public TEnum Value() + { + try + { + return JsonSerializer.Deserialize(this.Json, ModelBase.SerializerOptions) + ?? throw new ImageKitInvalidDataException($"{nameof(this.Json)} cannot be null"); + } + catch (JsonException e) + { + throw new ImageKitInvalidDataException( + $"{this.Json} must be of type {typeof(TRaw).FullName}", + e + ); + } + } + + /// + /// Verifies that this instance's raw value is a member of . + /// + /// + /// Thrown when this instance's raw value isn't a member of . + /// + /// + public void Validate() + { + if (!Enum.IsDefined(typeof(TEnum), Value())) + { + throw new ImageKitInvalidDataException("Invalid enum value"); + } + } + + public virtual bool Equals(ApiEnum? other) + { + return other != null && JsonElement.DeepEquals(this.Json, other.Json); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + public override int GetHashCode() + { + return 0; + } + + public static implicit operator TRaw(ApiEnum value) => value.Raw(); + + public static implicit operator TEnum(ApiEnum value) => value.Value(); + + public static implicit operator ApiEnum(TRaw value) => + new(JsonSerializer.SerializeToElement(value, ModelBase.SerializerOptions)); + + public static implicit operator ApiEnum(TEnum value) => + new(JsonSerializer.SerializeToElement(value, ModelBase.SerializerOptions)); +} + +sealed class ApiEnumConverter : JsonConverter> + where TEnum : struct, Enum +{ + public override ApiEnum Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options + ) + { + return new(JsonSerializer.Deserialize(ref reader, options)); + } + + public override void Write( + Utf8JsonWriter writer, + ApiEnum value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} diff --git a/src/Imagekit/Core/BinaryContent.cs b/src/Imagekit/Core/BinaryContent.cs new file mode 100644 index 00000000..8e147178 --- /dev/null +++ b/src/Imagekit/Core/BinaryContent.cs @@ -0,0 +1,25 @@ +using System.IO; +using System.Net.Http.Headers; + +namespace Imagekit.Core; + +/// +/// A class representing a binary stream of data with its associated (optional) file +/// name and content type. +/// +public sealed record class BinaryContent +{ + public required Stream Stream { get; init; } + public string? FileName { get; init; } + public MediaTypeHeaderValue ContentType { get; set; } = new("application/octet-stream"); + + public static implicit operator BinaryContent(Stream stream) => + new() + { + Stream = stream, + FileName = stream is FileStream fileStream ? Path.GetFileName(fileStream.Name) : null, + }; + + public static implicit operator BinaryContent(byte[] bytes) => + new() { Stream = new MemoryStream(bytes) }; +} diff --git a/src/Imagekit/Core/ClientOptions.cs b/src/Imagekit/Core/ClientOptions.cs new file mode 100644 index 00000000..e5fa096e --- /dev/null +++ b/src/Imagekit/Core/ClientOptions.cs @@ -0,0 +1,153 @@ +using System; +using System.Net.Http; +using Imagekit.Exceptions; + +namespace Imagekit.Core; + +/// +/// A class representing the SDK client configuration. +/// +public record struct ClientOptions() +{ + /// + /// The default value used for . + /// + public static readonly int DefaultMaxRetries = 2; + + /// + /// The default value used for . + /// + public static readonly TimeSpan DefaultTimeout = TimeSpan.FromMinutes(1); + + /// + /// The HTTP client to use for making requests in the SDK. + /// + /// Note: The HttpClient has a built-in timeout, which defaults to 100 seconds. + /// When passing a custom HttpClient, this timeout may conflict with the SDK's + /// own timeout handler and cause premature cancellation. + /// + public HttpClient HttpClient { get; set; } = + new(new HttpClientHandler() { AutomaticDecompression = DecompressionMethods.Available }) + { + Timeout = global::System.Threading.Timeout.InfiniteTimeSpan, + }; + + Lazy _baseUrl = new(() => + Environment.GetEnvironmentVariable("IMAGE_KIT_BASE_URL") ?? EnvironmentUrl.Production + ); + + /// + /// The base URL to use for every request. + /// + /// Defaults to the production environment: + /// + public string BaseUrl + { + readonly get { return _baseUrl.Value; } + set { _baseUrl = new(() => value); } + } + + /// + /// Whether to validate response bodies before returning them. + /// + /// Defaults to false, which means the shape of the response body will not be validated upfront. + /// Instead, validation will only occur for the parts of the response body that are accessed. + /// + /// Note that when set to true, the response body is only validated if the response is + /// deserialized. Methods that don't eagerly deserialize the response, such as those on + /// , don't perform validation until deserialization + /// is triggered. + /// + public bool ResponseValidation { get; set; } = false; + + /// + /// The maximum number of times to retry failed requests, with a short exponential backoff between requests. + /// + /// + /// Only the following error types are retried: + /// + /// Connection errors (for example, due to a network connectivity problem) + /// 408 Request Timeout + /// 409 Conflict + /// 429 Rate Limit + /// 5xx Internal + /// + /// + /// + /// The API may also explicitly instruct the SDK to retry or not retry a request. + /// + /// Defaults to 2 when null. Set to 0 to + /// disable retries, which also ignores API instructions to retry. + /// + public int? MaxRetries { get; set; } = null; + + /// + /// Sets the maximum time allowed for a complete HTTP call, not including retries. + /// + /// This includes resolving DNS, connecting, writing the request body, server processing, as + /// well as reading the response body. + /// + /// Defaults to TimeSpan.FromMinutes(1) when null. + /// + public TimeSpan? Timeout { get; set; } = null; + + /// + /// Your ImageKit private API key (starts with `private_`). You can find this + /// in the [ImageKit dashboard](https://imagekit.io/dashboard/developer/api-keys). + /// + Lazy _privateKey = new(() => + Environment.GetEnvironmentVariable("IMAGEKIT_PRIVATE_KEY") + ?? throw new ImageKitInvalidDataException( + string.Format("{0} cannot be null", nameof(PrivateKey)), + new ArgumentNullException(nameof(PrivateKey)) + ) + ); + + /// + /// Your ImageKit private API key (starts with `private_`). You can find this + /// in the [ImageKit dashboard](https://imagekit.io/dashboard/developer/api-keys). + /// + public string PrivateKey + { + readonly get { return _privateKey.Value; } + set { _privateKey = new(() => value); } + } + + /// + /// ImageKit uses your API key as username and ignores the password. The SDK + /// sets a dummy value. You can ignore this field. + /// + Lazy _password = new(() => + Environment.GetEnvironmentVariable("OPTIONAL_IMAGEKIT_IGNORES_THIS") ?? "do_not_set" + ); + + /// + /// ImageKit uses your API key as username and ignores the password. The SDK + /// sets a dummy value. You can ignore this field. + /// + public string? Password + { + readonly get { return _password.Value; } + set { _password = new(() => value); } + } + + /// + /// Your ImageKit webhook secret for verifying webhook signatures (starts with + /// `whsec_`). You can find this in the [ImageKit dashboard](https://imagekit.io/dashboard/developer/webhooks). + /// Only required if you're using webhooks. + /// + Lazy _webhookSecret = new(() => + Environment.GetEnvironmentVariable("IMAGEKIT_WEBHOOK_SECRET") + ); + + /// + /// Your ImageKit webhook secret for verifying webhook signatures (starts with + /// `whsec_`). You can find this in the [ImageKit dashboard](https://imagekit.io/dashboard/developer/webhooks). + /// Only required if you're using webhooks. + /// + public string? WebhookSecret + { + readonly get { return _webhookSecret.Value; } + set { _webhookSecret = new(() => value); } + } +} diff --git a/src/Imagekit/Core/DecompressionMethods.cs b/src/Imagekit/Core/DecompressionMethods.cs new file mode 100644 index 00000000..048568d0 --- /dev/null +++ b/src/Imagekit/Core/DecompressionMethods.cs @@ -0,0 +1,49 @@ +using System.IO; +using System.IO.Compression; +using Net = System.Net; + +namespace Imagekit.Core; + +static class DecompressionMethods +{ + internal static readonly Net::DecompressionMethods Available; + + static DecompressionMethods() + { + try + { + // Minimal valid GZip payload (empty body). + var gzipPayload = new byte[] + { + 0x1f, + 0x8b, + 0x08, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x03, + 0x03, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + }; + using var memoryStream = new MemoryStream(gzipPayload); + using var gzipStream = new GZipStream(memoryStream, CompressionMode.Decompress); + gzipStream.CopyTo(Stream.Null); + Available = Net::DecompressionMethods.GZip; + } + catch + { + Available = Net::DecompressionMethods.None; + } + } +} diff --git a/src/Imagekit/Core/EnvironmentUrl.cs b/src/Imagekit/Core/EnvironmentUrl.cs new file mode 100644 index 00000000..e3d88118 --- /dev/null +++ b/src/Imagekit/Core/EnvironmentUrl.cs @@ -0,0 +1,6 @@ +namespace Imagekit.Core; + +public static class EnvironmentUrl +{ + public static readonly string Production = "https://api.imagekit.io"; +} diff --git a/src/Imagekit/Core/FriendlyJsonPrinter.cs b/src/Imagekit/Core/FriendlyJsonPrinter.cs new file mode 100644 index 00000000..afe5b60d --- /dev/null +++ b/src/Imagekit/Core/FriendlyJsonPrinter.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; + +namespace Imagekit.Core; + +static class FriendlyJsonPrinter +{ + public static JsonElement PrintValue(JsonElement value) => value; + + public static JsonElement PrintValue(IReadOnlyDictionary value) => + JsonSerializer.SerializeToElement(value); + + public static JsonElement PrintValue(IReadOnlyList value) => + JsonSerializer.SerializeToElement(value); + + public static JsonElement PrintValue(IReadOnlyDictionary value) + { + int binaryContentCount = 0; + var ret = new Dictionary(); + foreach (var item in value) + { + ret[item.Key] = PrintValue( + item.Value.Json, + item.Value.BinaryContents, + ref binaryContentCount + ); + } + return PrintValue(ret); + } + + public static JsonElement PrintValue(MultipartJsonElement value) + { + int binaryContentCount = 0; + return PrintValue(value.Json, value.BinaryContents, ref binaryContentCount); + } + + static JsonElement PrintValue( + JsonElement json, + IReadOnlyDictionary binaryContent, + ref int binaryContentCount + ) + { + switch (json.ValueKind) + { + case JsonValueKind.Undefined: + case JsonValueKind.Null: + case JsonValueKind.Number: + case JsonValueKind.True: + case JsonValueKind.False: + return json; + case JsonValueKind.String: + return json.TryGetGuid(out var guid) && binaryContent.ContainsKey(guid) + ? JsonSerializer.SerializeToElement($"[Binary Content {binaryContentCount++}]") + : json; + case JsonValueKind.Object: + { + var ret = new Dictionary(); + foreach (var item in json.EnumerateObject()) + { + ret[item.Name] = PrintValue(item.Value, binaryContent, ref binaryContentCount); + } + return PrintValue(ret); + } + case JsonValueKind.Array: + { + var ret = new List(); + foreach (var item in json.EnumerateArray()) + { + ret.Add(PrintValue(item, binaryContent, ref binaryContentCount)); + } + return PrintValue(ret); + } + default: + throw new InvalidOperationException("Unreachable"); + } + } +} diff --git a/src/Imagekit/Core/FrozenDictionaryConverterFactory.cs b/src/Imagekit/Core/FrozenDictionaryConverterFactory.cs new file mode 100644 index 00000000..6bc82d86 --- /dev/null +++ b/src/Imagekit/Core/FrozenDictionaryConverterFactory.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Imagekit.Core; + +sealed class FrozenDictionaryConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) + { + if (!typeToConvert.IsGenericType) + { + return false; + } + + var genericTypeDefinition = typeToConvert.GetGenericTypeDefinition(); + return genericTypeDefinition == typeof(FrozenDictionary<,>); + } + + public override JsonConverter? CreateConverter( + Type typeToConvert, + JsonSerializerOptions options + ) + { + var keyType = typeToConvert.GetGenericArguments()[0]; + var valueType = typeToConvert.GetGenericArguments()[1]; + + var converterType = typeof(FrozenDictionaryConverter<,>).MakeGenericType( + keyType, + valueType + ); + return (JsonConverter?)Activator.CreateInstance(converterType); + } +} + +sealed class FrozenDictionaryConverter : JsonConverter> + where TKey : notnull +{ + public override FrozenDictionary? Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options + ) + { + var dictionary = JsonSerializer.Deserialize>(ref reader, options); + if (dictionary == null) + { + return null; + } + + return FrozenDictionary.ToFrozenDictionary(dictionary); + } + + public override void Write( + Utf8JsonWriter writer, + FrozenDictionary value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value, typeof(IReadOnlyDictionary), options); + } +} diff --git a/src/Imagekit/Core/HttpRequest.cs b/src/Imagekit/Core/HttpRequest.cs new file mode 100644 index 00000000..b5079abf --- /dev/null +++ b/src/Imagekit/Core/HttpRequest.cs @@ -0,0 +1,26 @@ +using System.Net.Http; + +namespace Imagekit.Core; + +public sealed class HttpRequest

+ where P : ParamsBase +{ + public required HttpMethod Method { get; init; } + + public required P Params { get; init; } + + public override string ToString() => + string.Format("Method: {0}\n{1}", this.Method.ToString(), this.Params.ToString()); + + public override bool Equals(object? obj) + { + if (obj is not HttpRequest

other) + { + return false; + } + + return this.Method.Equals(other.Method) && this.Params.Equals(other.Params); + } + + public override int GetHashCode() => 0; +} diff --git a/src/Imagekit/Core/HttpResponse.cs b/src/Imagekit/Core/HttpResponse.cs new file mode 100644 index 00000000..535ae233 --- /dev/null +++ b/src/Imagekit/Core/HttpResponse.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Runtime.CompilerServices; +using System.Text.Json; +using System.Threading.Tasks; +using Imagekit.Exceptions; +using Threading = System.Threading; + +namespace Imagekit.Core; + +public class HttpResponse : IDisposable +{ + public required HttpResponseMessage RawMessage { get; init; } + + public IEnumerable>> Headers + { + get { return RawMessage.Headers; } + } + + public bool IsSuccessStatusCode + { + get { return RawMessage.IsSuccessStatusCode; } + } + + public HttpStatusCode StatusCode + { + get { return RawMessage.StatusCode; } + } + + public Threading::CancellationToken CancellationToken { get; init; } = default; + + public IEnumerable GetHeaderValues(string name) => RawMessage.Headers.GetValues(name); + + public bool TryGetHeaderValues( + string name, + [NotNullWhen(true)] out IEnumerable? values + ) => RawMessage.Headers.TryGetValues(name, out values); + + public sealed override string ToString() => this.RawMessage.ToString(); + + public override bool Equals(object? obj) + { + if (obj is not HttpResponse other) + { + return false; + } + + return this.RawMessage.Equals(other.RawMessage); + } + + public override int GetHashCode() => this.RawMessage.GetHashCode(); + + public async Task Deserialize(Threading::CancellationToken cancellationToken = default) + { + using var cts = Threading::CancellationTokenSource.CreateLinkedTokenSource( + this.CancellationToken, + cancellationToken + ); + try + { + return await JsonSerializer + .DeserializeAsync( + await this.ReadAsStream(cts.Token).ConfigureAwait(false), + ModelBase.SerializerOptions, + cts.Token + ) + .ConfigureAwait(false) + ?? throw new ImageKitInvalidDataException("Response cannot be null"); + } + catch (HttpRequestException e) + { + throw new ImageKitIOException("I/O Exception", e); + } + } + + public async Task ReadAsStream(Threading::CancellationToken cancellationToken = default) + { + using var cts = Threading::CancellationTokenSource.CreateLinkedTokenSource( + this.CancellationToken, + cancellationToken + ); + return await RawMessage.Content.ReadAsStreamAsync( +#if NET + cts.Token +#endif + ).ConfigureAwait(false); + } + + public async Task ReadAsString(Threading::CancellationToken cancellationToken = default) + { + using var cts = Threading::CancellationTokenSource.CreateLinkedTokenSource( + this.CancellationToken, + cancellationToken + ); + return await RawMessage.Content.ReadAsStringAsync( +#if NET + cts.Token +#endif + ).ConfigureAwait(false); + } + + public void Dispose() + { + this.RawMessage.Dispose(); + GC.SuppressFinalize(this); + } +} + +public sealed class HttpResponse : HttpResponse +{ + readonly Func> _deserialize; + + internal HttpResponse(Func> deserialize) + { + this._deserialize = deserialize; + } + + [SetsRequiredMembers] + internal HttpResponse( + HttpResponse response, + Func> deserialize + ) + : this(deserialize) + { + this.RawMessage = response.RawMessage; + this.CancellationToken = response.CancellationToken; + } + + public Task Deserialize(Threading::CancellationToken cancellationToken = default) + { + using var cts = Threading::CancellationTokenSource.CreateLinkedTokenSource( + this.CancellationToken, + cancellationToken + ); + return this._deserialize(cts.Token); + } +} + +public sealed class StreamingHttpResponse : HttpResponse +{ + readonly Func> _enumerate; + + internal StreamingHttpResponse( + Func> enumerate + ) + { + this._enumerate = enumerate; + } + + [SetsRequiredMembers] + internal StreamingHttpResponse( + HttpResponse response, + Func> enumerate + ) + : this(enumerate) + { + this.RawMessage = response.RawMessage; + this.CancellationToken = response.CancellationToken; + } + + public async IAsyncEnumerable Enumerate( + [EnumeratorCancellationAttribute] Threading::CancellationToken cancellationToken = default + ) + { + using var cts = Threading::CancellationTokenSource.CreateLinkedTokenSource( + this.CancellationToken, + cancellationToken + ); + await foreach (var item in this._enumerate(cts.Token)) + { + yield return item; + } + } +} diff --git a/src/Imagekit/Core/IPage.cs b/src/Imagekit/Core/IPage.cs new file mode 100644 index 00000000..fe38cf8c --- /dev/null +++ b/src/Imagekit/Core/IPage.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Exceptions; + +namespace Imagekit.Core; + +///

+/// An interface representing a single page, with items of type , from a +/// paginated endpoint response. +/// +public interface IPage +{ + /// + /// The items in this page. + /// + IReadOnlyList Items { get; } + + /// + /// Returns whether there's another page after this one. + /// + /// The method doesn't make requests so the result depends entirely on the + /// data in this page. If a significant amount of time has passed between requesting + /// this page and calling this method, then the result could be stale. + /// + bool HasNext(); + + /// + /// Returns the page after this one by making another request. + /// + /// + /// Thrown when it's impossible to get the next page. This exception is avoidable by calling + /// first. + /// + /// + Task> Next(CancellationToken cancellationToken = default); + + /// + /// Validates that the page was constructed with a valid response (based on its own + /// Validate method). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + void Validate(); + +#if NET + /// + public IAsyncEnumerable Paginate(CancellationToken cancellationToken = default) => + IPageExtensions.Paginate(this, cancellationToken); +#endif +} diff --git a/src/Imagekit/Core/JsonDictionary.cs b/src/Imagekit/Core/JsonDictionary.cs new file mode 100644 index 00000000..6074a2bd --- /dev/null +++ b/src/Imagekit/Core/JsonDictionary.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using Imagekit.Exceptions; + +namespace Imagekit.Core; + +/// +/// A dictionary that holds JSON data. +/// +/// It can be mutated and then frozen once no more mutations are expected. +/// This is useful for allowing the dictionary to be modified by a class's +/// init properties, but then preventing it from being modified afterwards. +/// +/// It also caches data deserialization for performance. +/// +sealed class JsonDictionary +{ + IReadOnlyDictionary _rawData; + + readonly ConcurrentDictionary _deserializedData; + + Dictionary MutableRawData + { + get + { + if (_rawData is Dictionary dictionary) + { + return dictionary; + } + throw new InvalidOperationException("Can't mutate after freezing."); + } + } + + public JsonDictionary() + { + _rawData = new Dictionary(); + _deserializedData = new(); + } + + public JsonDictionary(IReadOnlyDictionary dictionary) + { + _rawData = Enumerable.ToDictionary(dictionary, (e) => e.Key, (e) => e.Value); + _deserializedData = new(); + } + + public JsonDictionary(FrozenDictionary dictionary) + { + _rawData = dictionary; + _deserializedData = new(); + } + + public JsonDictionary(JsonDictionary dictionary) + { + _rawData = Enumerable.ToDictionary(dictionary._rawData, (e) => e.Key, (e) => e.Value); + _deserializedData = new(dictionary._deserializedData); + } + + /// + /// Freezes this dictionary and returns a readonly view of it. + /// + /// Future calls to mutating methods on this class will throw + /// . + /// + public IReadOnlyDictionary Freeze() + { + if (_rawData is FrozenDictionary dictionary) + { + return dictionary; + } + + var frozenRawData = FrozenDictionary.ToFrozenDictionary(_rawData); + _rawData = frozenRawData; + return frozenRawData; + } + + public void Set(string key, T value) + { + MutableRawData[key] = JsonSerializer.SerializeToElement(value, ModelBase.SerializerOptions); + _deserializedData[key] = value; + } + + public T GetNotNullClass(string key) + where T : class + { + if (_deserializedData.TryGetValue(key, out var cached) && cached is T t) + { + return t; + } + if (!_rawData.TryGetValue(key, out JsonElement element)) + { + throw new ImageKitInvalidDataException($"'{key}' cannot be absent"); + } + T deserialized = WrappedJsonSerializer.GetNotNullClass(element, key); + _deserializedData[key] = deserialized; + return deserialized; + } + + public T GetNotNullStruct(string key) + where T : struct + { + if (_deserializedData.TryGetValue(key, out var cached) && cached is T t) + { + return t; + } + if (!_rawData.TryGetValue(key, out JsonElement element)) + { + throw new ImageKitInvalidDataException($"'{key}' cannot be absent"); + } + T deserialized = WrappedJsonSerializer.GetNotNullStruct(element, key); + _deserializedData[key] = deserialized; + return deserialized; + } + + public T? GetNullableClass(string key) + where T : class + { + if (_deserializedData.TryGetValue(key, out var cached) && (cached == null || cached is T)) + { + return (T?)cached; + } + if (!_rawData.TryGetValue(key, out JsonElement element)) + { + _deserializedData[key] = null; + return null; + } + T? deserialized = WrappedJsonSerializer.GetNullableClass(element, key); + _deserializedData[key] = deserialized; + return deserialized; + } + + public T? GetNullableStruct(string key) + where T : struct + { + if (_deserializedData.TryGetValue(key, out var cached) && (cached == null || cached is T)) + { + return (T?)cached; + } + if (!_rawData.TryGetValue(key, out JsonElement element)) + { + _deserializedData[key] = null; + return null; + } + T? deserialized = WrappedJsonSerializer.GetNullableStruct(element, key); + _deserializedData[key] = deserialized; + return deserialized; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this._rawData), + ModelBase.ToStringSerializerOptions + ); + + public override bool Equals(object? obj) + { + if (obj is not JsonDictionary other || _rawData.Count != other._rawData.Count) + { + return false; + } + + foreach (var item in _rawData) + { + if (!other._rawData.TryGetValue(item.Key, out var otherValue)) + { + return false; + } + + if (!JsonElement.DeepEquals(item.Value, otherValue)) + { + return false; + } + } + + return true; + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Core/JsonModel.cs b/src/Imagekit/Core/JsonModel.cs new file mode 100644 index 00000000..ed57a19f --- /dev/null +++ b/src/Imagekit/Core/JsonModel.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; +using System.Text.Json; + +namespace Imagekit.Core; + +/// +/// The base class for all API objects that are serialized as JSON objects. +/// +/// API objects such as enums and unions do not inherit from this class. +/// +public abstract record class JsonModel : ModelBase +{ + private protected JsonDictionary _rawData = new(); + + /// + /// The backing JSON properties of the instance. + /// + public IReadOnlyDictionary RawData + { + get { return this._rawData.Freeze(); } + } + + protected JsonModel(JsonModel jsonModel) + : base(jsonModel) + { + this._rawData = new(jsonModel._rawData); + } + + public sealed override string ToString() => this._rawData.ToString(); + + public virtual bool Equals(JsonModel? other) + { + if (other == null) + { + return false; + } + + return this._rawData.Equals(other._rawData); + } + + public override int GetHashCode() => this._rawData.GetHashCode(); +} + +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with breaking +/// changes in non-major versions. We may add new methods in the future that cause +/// existing derived classes to break. +/// +/// NOTE: This interface is in the style of a factory instance instead of using +/// abstract static methods because .NET Standard 2.0 doesn't support abstract static methods. +/// +interface IFromRawJson +{ + /// + /// Returns an instance constructed from the given raw JSON properties. + /// + /// Required field and type mismatches are not checked. In these cases accessing + /// the relevant properties of the constructed instance may throw. + /// + /// This method is useful for constructing an instance from already serialized + /// data or for sending arbitrary data to the API (e.g. for undocumented or not + /// yet supported properties or values). + /// + T FromRawUnchecked(IReadOnlyDictionary rawData); +} diff --git a/src/Imagekit/Core/JsonModelConverter.cs b/src/Imagekit/Core/JsonModelConverter.cs new file mode 100644 index 00000000..882e5eca --- /dev/null +++ b/src/Imagekit/Core/JsonModelConverter.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Imagekit.Core; + +sealed class JsonModelConverter : JsonConverter + where TModel : JsonModel + where TFromRaw : IFromRawJson, new() +{ + public override TModel? Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options + ) + { + var rawData = JsonSerializer.Deserialize>( + ref reader, + options + ); + if (rawData == null) + return null; + + return new TFromRaw().FromRawUnchecked(rawData); + } + + public override void Write(Utf8JsonWriter writer, TModel value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.RawData, options); + } +} diff --git a/src/Imagekit/Core/ModelBase.cs b/src/Imagekit/Core/ModelBase.cs new file mode 100644 index 00000000..9336ff94 --- /dev/null +++ b/src/Imagekit/Core/ModelBase.cs @@ -0,0 +1,178 @@ +using System.Text.Json; +using Imagekit.Exceptions; +using Imagekit.Models; +using Imagekit.Models.Cache.Invalidation; +using Assets = Imagekit.Models.Assets; +using CustomMetadataFields = Imagekit.Models.CustomMetadataFields; +using Files = Imagekit.Models.Files; +using Job = Imagekit.Models.Folders.Job; +using V2Files = Imagekit.Models.Beta.V2.Files; +using Webhooks = Imagekit.Models.Webhooks; + +namespace Imagekit.Core; + +/// +/// The base class for all API objects with properties. +/// +/// API objects such as enums do not inherit from this class. +/// +public abstract record class ModelBase +{ + protected ModelBase(ModelBase modelBase) + { + // Nothing to copy. Just so that subclasses can define copy constructors. + } + + internal static readonly JsonSerializerOptions SerializerOptions = new() + { + Converters = + { + new FrozenDictionaryConverterFactory(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter< + string, + Files::FileUploadResponseExtensionStatusAIAutoDescription + >(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter< + string, + Files::FileUploadResponseExtensionStatusGoogleAutoTagging + >(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter< + string, + Webhooks::UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType + >(), + new ApiEnumConverter< + string, + Webhooks::UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol + >(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter< + string, + Webhooks::VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType + >(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter< + string, + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType + >(), + new ApiEnumConverter(), + new ApiEnumConverter< + string, + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec + >(), + new ApiEnumConverter< + string, + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat + >(), + new ApiEnumConverter< + string, + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol + >(), + new ApiEnumConverter< + string, + Webhooks::VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec + >(), + new ApiEnumConverter< + string, + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType + >(), + new ApiEnumConverter< + string, + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec + >(), + new ApiEnumConverter< + string, + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat + >(), + new ApiEnumConverter< + string, + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol + >(), + new ApiEnumConverter< + string, + Webhooks::VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec + >(), + }, + }; + + internal static readonly JsonSerializerOptions ToStringSerializerOptions = new( + SerializerOptions + ) + { + WriteIndented = true, + }; + + /// + /// Validates that all required fields are set and that each field's value is of the expected type. + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public abstract void Validate(); +} diff --git a/src/Imagekit/Core/MultipartJsonDictionary.cs b/src/Imagekit/Core/MultipartJsonDictionary.cs new file mode 100644 index 00000000..296cc16d --- /dev/null +++ b/src/Imagekit/Core/MultipartJsonDictionary.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using Imagekit.Exceptions; + +namespace Imagekit.Core; + +/// +/// A dictionary that holds mixed JSON and binary content. +/// +/// It can be mutated and then frozen once no more mutations are expected. +/// This is useful for allowing the dictionary to be modified by a class's +/// init properties, but then preventing it from being modified afterwards. +/// +/// It also caches data deserialization for performance. +/// +sealed class MultipartJsonDictionary +{ + IReadOnlyDictionary _rawData; + + readonly ConcurrentDictionary _deserializedData; + + Dictionary MutableRawData + { + get + { + if (_rawData is Dictionary dictionary) + { + return dictionary; + } + throw new InvalidOperationException("Can't mutate after freezing."); + } + } + + public MultipartJsonDictionary() + { + _rawData = new Dictionary(); + _deserializedData = new(); + } + + public MultipartJsonDictionary(IReadOnlyDictionary dictionary) + { + _rawData = Enumerable.ToDictionary(dictionary, (e) => e.Key, (e) => e.Value); + _deserializedData = new(); + } + + public MultipartJsonDictionary(FrozenDictionary dictionary) + { + _rawData = dictionary; + _deserializedData = new(); + } + + public MultipartJsonDictionary(MultipartJsonDictionary dictionary) + { + _rawData = Enumerable.ToDictionary(dictionary._rawData, (e) => e.Key, (e) => e.Value); + _deserializedData = new(dictionary._deserializedData); + } + + /// + /// Freezes this dictionary and returns a readonly view of it. + /// + /// Future calls to mutating methods on this class will throw + /// . + /// + public IReadOnlyDictionary Freeze() + { + if (_rawData is FrozenDictionary dictionary) + { + return dictionary; + } + + var frozenRawData = FrozenDictionary.ToFrozenDictionary(_rawData); + _rawData = frozenRawData; + return frozenRawData; + } + + public void Set(string key, T value) + { + MutableRawData[key] = MultipartJsonSerializer.SerializeToElement( + value, + ModelBase.SerializerOptions + ); + _deserializedData[key] = value; + } + + public T GetNotNullClass(string key) + where T : class + { + if (_deserializedData.TryGetValue(key, out var cached) && cached is T t) + { + return t; + } + if (!_rawData.TryGetValue(key, out MultipartJsonElement element)) + { + throw new ImageKitInvalidDataException($"'{key}' cannot be absent"); + } + T? deserialized = WrappedMultipartJsonSerializer.GetNotNullClass(element, key); + _deserializedData[key] = deserialized; + return deserialized; + } + + public T GetNotNullStruct(string key) + where T : struct + { + if (_deserializedData.TryGetValue(key, out var cached) && cached is T t) + { + return t; + } + if (!_rawData.TryGetValue(key, out MultipartJsonElement element)) + { + throw new ImageKitInvalidDataException($"'{key}' cannot be absent"); + } + T deserialized = WrappedMultipartJsonSerializer.GetNotNullStruct(element, key); + _deserializedData[key] = deserialized; + return deserialized; + } + + public T? GetNullableClass(string key) + where T : class + { + if (_deserializedData.TryGetValue(key, out var cached) && (cached == null || cached is T)) + { + return (T?)cached; + } + if (!_rawData.TryGetValue(key, out MultipartJsonElement element)) + { + _deserializedData[key] = null; + return null; + } + T? deserialized = WrappedMultipartJsonSerializer.GetNullableClass(element, key); + _deserializedData[key] = deserialized; + return deserialized; + } + + public T? GetNullableStruct(string key) + where T : struct + { + if (_deserializedData.TryGetValue(key, out var cached) && (cached == null || cached is T)) + { + return (T?)cached; + } + if (!_rawData.TryGetValue(key, out MultipartJsonElement element)) + { + _deserializedData[key] = null; + return null; + } + T? deserialized = WrappedMultipartJsonSerializer.GetNullableStruct(element, key); + _deserializedData[key] = deserialized; + return deserialized; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this._rawData), + ModelBase.ToStringSerializerOptions + ); + + public override bool Equals(object? obj) + { + if (obj is not MultipartJsonDictionary other || _rawData.Count != other._rawData.Count) + { + return false; + } + + foreach (var item in _rawData) + { + if (!other._rawData.TryGetValue(item.Key, out var otherValue)) + { + return false; + } + + if (!MultipartJsonElement.DeepEquals(item.Value, otherValue)) + { + return false; + } + } + + return true; + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Core/MultipartJsonElement.cs b/src/Imagekit/Core/MultipartJsonElement.cs new file mode 100644 index 00000000..ade91ec0 --- /dev/null +++ b/src/Imagekit/Core/MultipartJsonElement.cs @@ -0,0 +1,447 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading; + +namespace Imagekit.Core; + +/// +/// A that can contain . +/// +/// Use to construct or read instances of this class. +/// +public readonly struct MultipartJsonElement +{ + /// + /// A with placeholders + /// for . + /// + internal JsonElement Json { get; init; } = default; + + /// + /// A dictionary from placeholder string in the JSON to + /// . + /// + internal IReadOnlyDictionary BinaryContents { get; init; } = + FrozenDictionary.ToFrozenDictionary(new Dictionary()); + + public static implicit operator MultipartJsonElement(JsonElement json) => new() { Json = json }; + + public MultipartJsonElement() { } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this), + ModelBase.ToStringSerializerOptions + ); + + public static bool DeepEquals(MultipartJsonElement a, MultipartJsonElement b) => + MultipartJsonElement.DeepEqualsInner(a.Json, a.BinaryContents, b.Json, b.BinaryContents); + + static bool DeepEqualsInner( + JsonElement jsonA, + IReadOnlyDictionary binaryA, + JsonElement jsonB, + IReadOnlyDictionary binaryB + ) + { + if (jsonA.ValueKind != jsonB.ValueKind) + { + return false; + } + + switch (jsonA.ValueKind) + { + case JsonValueKind.Undefined: + case JsonValueKind.Null: + case JsonValueKind.True: + case JsonValueKind.False: + return true; + case JsonValueKind.Number: + return JsonElement.DeepEquals(jsonA, jsonB); + case JsonValueKind.String: + BinaryContent? aContent = null; + + BinaryContent? bContent = null; + + if (jsonA.TryGetGuid(out var guidA) && binaryA.TryGetValue(guidA, out var a)) + { + aContent = a; + } + + if (jsonB.TryGetGuid(out var guidB) && binaryB.TryGetValue(guidB, out var b)) + { + bContent = b; + } + + if (aContent != null && bContent != null) + { + return aContent == bContent; + } + else if (aContent == null && bContent == null) + { + return jsonA.GetString() == jsonB.GetString(); + } + else + { + return false; + } + case JsonValueKind.Object: + Dictionary propertiesA = new(); + + foreach (var item1 in jsonA.EnumerateObject()) + { + propertiesA[item1.Name] = item1.Value; + } + + Dictionary propertiesB = new(); + + foreach (var item1 in jsonB.EnumerateObject()) + { + propertiesB[item1.Name] = item1.Value; + } + + if (propertiesA.Count != propertiesB.Count) + { + return false; + } + + foreach (var property in propertiesA) + { + if (!propertiesB.TryGetValue(property.Key, out var b1)) + { + return false; + } + + if (!MultipartJsonElement.DeepEqualsInner(property.Value, binaryA, b1, binaryB)) + { + return false; + } + } + + return true; + case JsonValueKind.Array: + if (jsonA.GetArrayLength() != jsonB.GetArrayLength()) + { + return false; + } + + var i = 0; + foreach (var item in jsonA.EnumerateArray()) + { + if (!MultipartJsonElement.DeepEqualsInner(item, binaryA, jsonB[i], binaryB)) + { + return false; + } + + i++; + } + + return true; + default: + throw new InvalidOperationException("Unreachable"); + } + } +} + +/// +/// A serializer for mixed JSON and binary content. +/// +public static class MultipartJsonSerializer +{ + /// + /// The current dictionary from placeholder string to to use for + /// serialization/deserialization. + /// + /// This isn't a local variable because we want to share + /// for performance. It's also a thread local to avoid data races between threads. + /// + static readonly ThreadLocal?> CurrentBinaryContents = new(() => + null + ); + + internal static Dictionary BinaryContents + { + get + { + return CurrentBinaryContents.Value + ?? throw new InvalidOperationException( + "Cannot convert BinaryContent without MultipartJsonSerializer" + ); + } + } + + static readonly ThreadLocal< + Dictionary + > MultipartSerializerOptionsCache = new(() => new()); + + static readonly JsonSerializerOptions DefaultMultipartSerializerOptions = + MultipartSerializerOptions(new()); + + static JsonSerializerOptions MultipartSerializerOptions(JsonSerializerOptions? options = null) + { + if (options == null) + { + return DefaultMultipartSerializerOptions; + } + + if (!MultipartSerializerOptionsCache.Value!.TryGetValue(options, out var multipartOptions)) + { + multipartOptions = new(options); + multipartOptions.Converters.Add(new BinaryContentConverter()); + multipartOptions.Converters.Add(new MultipartJsonElementConverter()); + MultipartSerializerOptionsCache.Value![options] = multipartOptions; + } + + return multipartOptions; + } + + public static MultipartJsonElement SerializeToElement( + T value, + JsonSerializerOptions? options = null + ) + { + var previousBinaryContents = CurrentBinaryContents.Value; + try + { + CurrentBinaryContents.Value = new(); + var element = JsonSerializer.SerializeToElement( + value, + MultipartSerializerOptions(options) + ); + return new() + { + Json = element, + BinaryContents = FrozenDictionary.ToFrozenDictionary(CurrentBinaryContents.Value!), + }; + } + finally + { + CurrentBinaryContents.Value = previousBinaryContents; + } + } + + public static T? Deserialize( + MultipartJsonElement element, + JsonSerializerOptions? options = null + ) + { + var previousBinaryContents = CurrentBinaryContents.Value; + try + { + CurrentBinaryContents.Value = Enumerable.ToDictionary( + element.BinaryContents, + (e) => e.Key, + (e) => e.Value + ); + return JsonSerializer.Deserialize(element.Json, MultipartSerializerOptions(options)); + } + finally + { + CurrentBinaryContents.Value = previousBinaryContents; + } + } + + public static MultipartFormDataContent Serialize( + T value, + JsonSerializerOptions? options = null + ) + { + MultipartFormDataContent formDataContent = new(); + var multipartElement = MultipartJsonSerializer.SerializeToElement(value, options); + void SerializeParts(string name, JsonElement element) + { + HttpContent? content; + string? fileName = null; + switch (element.ValueKind) + { + case JsonValueKind.Undefined: + case JsonValueKind.Null: + return; + case JsonValueKind.Number: + content = new StringContent(element.ToString()); + break; + case JsonValueKind.String: + if ( + element.TryGetGuid(out var guid) + && multipartElement.BinaryContents.TryGetValue(guid, out var binaryContent) + ) + { + content = new StreamContent(binaryContent.Stream); + content.Headers.ContentType = binaryContent.ContentType; + fileName = binaryContent.FileName; + } + else + { + content = new StringContent(element.ToString()); + } + break; + case JsonValueKind.True: + content = new StringContent("true"); + break; + case JsonValueKind.False: + content = new StringContent("false"); + break; + case JsonValueKind.Object: + foreach (var item in element.EnumerateObject()) + { + SerializeParts( + name == "" ? item.Name : string.Format("{0}[{1}]", name, item.Name), + item.Value + ); + } + return; + case JsonValueKind.Array: + var items = new List(); + foreach (var arrayItem in element.EnumerateArray()) + { + switch (arrayItem.ValueKind) + { + case JsonValueKind.Undefined: + case JsonValueKind.Null: + items.Add(""); + break; + case JsonValueKind.True: + items.Add("true"); + break; + case JsonValueKind.False: + items.Add("false"); + break; + case JsonValueKind.String: + if ( + arrayItem.TryGetGuid(out var itemGuid) + && multipartElement.BinaryContents.TryGetValue( + itemGuid, + out var itemBinaryContent + ) + ) + { + var itemContent = new StreamContent(itemBinaryContent.Stream); + itemContent.Headers.ContentType = itemBinaryContent.ContentType; + var itemFileName = itemBinaryContent.FileName; + if (name == "") + { + formDataContent.Add(itemContent); + } + else if (itemFileName == null) + { + formDataContent.Add(itemContent, $"{name}[]"); + } + else + { + formDataContent.Add(itemContent, $"{name}[]", itemFileName); + } + } + else + { + items.Add(arrayItem.ToString()); + } + break; + default: + throw new InvalidDataException("Unexpected element type in array"); + } + } + + if (items.Count > 0) + { + content = new StringContent(string.Join(",", items)); + } + else + { + content = null; + } + + break; + default: + throw new ArgumentOutOfRangeException(nameof(element)); + } + + if (content != null) + { + if (name == "") + { + formDataContent.Add(content); + } + else if (fileName == null) + { + formDataContent.Add(content, name); + } + else + { + formDataContent.Add(content, name, fileName); + } + } + } + SerializeParts("", multipartElement.Json); + return formDataContent; + } +} + +/// +/// A JSON converter that serializes/deserializes mixed JSON and binary content. +/// +/// It uses placeholder IDs (see ), which are written/read +/// to/from a thread-local dictionary, so it's expected that this converter is only invoked via +/// , which ensures the dictionary is set up correctly. +/// +sealed class MultipartJsonElementConverter : JsonConverter +{ + public override MultipartJsonElement Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options + ) => + new() + { + Json = JsonSerializer.Deserialize(ref reader, options), + BinaryContents = MultipartJsonSerializer.BinaryContents, + }; + + public override void Write( + Utf8JsonWriter writer, + MultipartJsonElement value, + JsonSerializerOptions options + ) + { + foreach (var item in value.BinaryContents) + { + MultipartJsonSerializer.BinaryContents.Add(item.Key, item.Value); + } + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// A JSON converter that serializes/deserializes binary content to/from placeholder IDs. +/// +/// The placeholder IDs are written/read to/from a thread-local dictionary, so it's expected that +/// this converter is only invoked via , which ensures the +/// dictionary is set up correctly. +/// +sealed class BinaryContentConverter : JsonConverter +{ + public override BinaryContent Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options + ) => + MultipartJsonSerializer.BinaryContents[ + JsonSerializer.Deserialize(ref reader, options) + ]; + + public override void Write( + Utf8JsonWriter writer, + BinaryContent value, + JsonSerializerOptions options + ) + { + var guid = Guid.NewGuid(); + MultipartJsonSerializer.BinaryContents[guid] = value; + JsonSerializer.Serialize(writer, guid, options); + } +} diff --git a/src/Imagekit/Core/MultipartJsonModel.cs b/src/Imagekit/Core/MultipartJsonModel.cs new file mode 100644 index 00000000..bec29096 --- /dev/null +++ b/src/Imagekit/Core/MultipartJsonModel.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; + +namespace Imagekit.Core; + +/// +/// The base class for all API objects that are serialized as a mix of JSON objects +/// and binary content. +/// +/// API objects such as enums and unions do not inherit from this class. +/// +public abstract record class MultipartJsonModel : ModelBase +{ + private protected MultipartJsonDictionary _rawData = new(); + + protected MultipartJsonModel(MultipartJsonModel jsonModel) + : base(jsonModel) + { + this._rawData = new(jsonModel._rawData); + } + + /// + /// The backing mix of JSON and binary content properties of the instance. + /// + public IReadOnlyDictionary RawData + { + get { return this._rawData.Freeze(); } + } + + public override int GetHashCode() + { + return 0; + } +} + +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with breaking +/// changes in non-major versions. We may add new methods in the future that cause +/// existing derived classes to break. +/// +/// NOTE: This interface is in the style of a factory instance instead of using +/// abstract static methods because .NET Standard 2.0 doesn't support abstract static methods. +/// +interface IFromRawMultipartJson +{ + /// + /// Returns an instance constructed from the given raw JSON properties. + /// + /// Required field and type mismatches are not checked. In these cases accessing + /// the relevant properties of the constructed instance may throw. + /// + /// This method is useful for constructing an instance from already serialized + /// data or for sending arbitrary data to the API (e.g. for undocumented or not + /// yet supported properties or values). + /// + T FromRawUnchecked(IReadOnlyDictionary rawData); +} diff --git a/src/Imagekit/Core/MultipartJsonModelConverter.cs b/src/Imagekit/Core/MultipartJsonModelConverter.cs new file mode 100644 index 00000000..0f25795d --- /dev/null +++ b/src/Imagekit/Core/MultipartJsonModelConverter.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Imagekit.Core; + +sealed class MultipartJsonModelConverter : JsonConverter + where TModel : MultipartJsonModel + where TFromRaw : IFromRawMultipartJson, new() +{ + public override TModel? Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options + ) + { + var rawData = JsonSerializer.Deserialize>( + ref reader, + options + ); + if (rawData == null) + return null; + + return new TFromRaw().FromRawUnchecked(rawData); + } + + public override void Write(Utf8JsonWriter writer, TModel value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.RawData, options); + } +} diff --git a/src/Imagekit/Core/ParamsBase.cs b/src/Imagekit/Core/ParamsBase.cs new file mode 100644 index 00000000..ac106ae5 --- /dev/null +++ b/src/Imagekit/Core/ParamsBase.cs @@ -0,0 +1,303 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; +using System.Net.Http; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.Json; +using System.Web; + +namespace Imagekit.Core; + +public abstract record class ParamsBase +{ + static readonly IReadOnlyDictionary defaultHeaders; + + static ParamsBase() + { + var runtime = GetRuntime(); + var headers = new Dictionary + { + ["User-Agent"] = GetUserAgent(), + ["X-Stainless-Arch"] = GetOSArch(), + ["X-Stainless-Lang"] = "csharp", + ["X-Stainless-OS"] = GetOS(), + ["X-Stainless-Package-Version"] = GetPackageVersion(), + ["X-Stainless-Runtime"] = runtime.Name, + ["X-Stainless-Runtime-Version"] = runtime.Version, + }; + + var customHeadersEnv = Environment.GetEnvironmentVariable("IMAGE_KIT_CUSTOM_HEADERS"); + if (customHeadersEnv != null) + { + foreach (var line in customHeadersEnv.Split('\n')) + { + var colon = line.IndexOf(':'); + if (colon >= 0) + { + headers[line.Substring(0, colon).Trim()] = line.Substring(colon + 1).Trim(); + } + } + } + + defaultHeaders = headers; + } + + private protected JsonDictionary _rawQueryData = new(); + + private protected JsonDictionary _rawHeaderData = new(); + + protected ParamsBase(ParamsBase paramsBase) + { + this._rawHeaderData = new(paramsBase._rawHeaderData); + this._rawQueryData = new(paramsBase._rawQueryData); + } + + public IReadOnlyDictionary RawQueryData + { + get { return this._rawQueryData.Freeze(); } + } + + public IReadOnlyDictionary RawHeaderData + { + get { return this._rawHeaderData.Freeze(); } + } + + public abstract Uri Url(ClientOptions options); + + protected static void AddQueryElementToCollection( + NameValueCollection collection, + string key, + JsonElement element + ) + { + switch (element.ValueKind) + { + case JsonValueKind.Undefined: + case JsonValueKind.Null: + collection.Add(key, ""); + break; + case JsonValueKind.String: + case JsonValueKind.Number: + collection.Add(key, element.ToString()); + break; + case JsonValueKind.True: + collection.Add(key, "true"); + break; + case JsonValueKind.False: + collection.Add(key, "false"); + break; + case JsonValueKind.Object: + foreach (var item in element.EnumerateObject()) + { + AddQueryElementToCollection( + collection, + string.Format("{0}[{1}]", key, item.Name), + item.Value + ); + } + break; + case JsonValueKind.Array: + collection.Add( + key, + string.Join( + ",", + Enumerable.Select( + element.EnumerateArray(), + x => + x.ValueKind switch + { + JsonValueKind.Null => "", + JsonValueKind.True => "true", + JsonValueKind.False => "false", + _ => x.GetString(), + } + ) + ) + ); + break; + } + } + + protected static void AddHeaderElementToRequest( + HttpRequestMessage request, + string key, + JsonElement element + ) + { + switch (element.ValueKind) + { + case JsonValueKind.Undefined: + case JsonValueKind.Null: + request.Headers.Add(key, ""); + break; + case JsonValueKind.String: + case JsonValueKind.Number: + request.Headers.Add(key, element.ToString()); + break; + case JsonValueKind.True: + request.Headers.Add(key, "true"); + break; + case JsonValueKind.False: + request.Headers.Add(key, "false"); + break; + case JsonValueKind.Object: + foreach (var item in element.EnumerateObject()) + { + AddHeaderElementToRequest( + request, + string.Format("{0}.{1}", key, item.Name), + item.Value + ); + } + break; + case JsonValueKind.Array: + foreach (var item in element.EnumerateArray()) + { + request.Headers.Add( + key, + item.ValueKind switch + { + JsonValueKind.Null => "", + JsonValueKind.True => "true", + JsonValueKind.False => "false", + _ => item.GetString(), + } + ); + } + break; + } + } + + internal string QueryString(ClientOptions options) + { + NameValueCollection collection = new(); + foreach (var item in this.RawQueryData) + { + ParamsBase.AddQueryElementToCollection(collection, item.Key, item.Value); + } + StringBuilder sb = new(); + bool first = true; + foreach (var key in collection.AllKeys) + { + foreach (var value in collection.GetValues(key) ?? []) + { + if (!first) + { + sb.Append('&'); + } + first = false; + sb.Append(HttpUtility.UrlEncode(key)); + sb.Append('='); + sb.Append(HttpUtility.UrlEncode(value)); + } + } + return sb.ToString(); + } + + internal abstract void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options); + + internal virtual HttpContent? BodyContent() + { + return null; + } + + internal static void AddDefaultHeaders(HttpRequestMessage request, ClientOptions options) + { + foreach (var header in defaultHeaders) + { + request.Headers.Add(header.Key, header.Value); + } + + if (options.PrivateKey != null && options.Password != null) + { + request.Headers.Add( + "Authorization", + string.Format( + "Basic {0}", + Convert.ToBase64String( + Encoding + .GetEncoding("ISO-8859-1") + .GetBytes( + string.Format("{0}:{1}", options.PrivateKey, options.Password) + ) + ) + ) + ); + } + request.Headers.Add( + "X-Stainless-Timeout", + (options.Timeout ?? ClientOptions.DefaultTimeout).TotalSeconds.ToString() + ); + } + + static string GetUserAgent() => $"{typeof(ImageKitClient).Name}/C# {GetPackageVersion()}"; + + static string GetPackageVersion() => + Assembly + .GetExecutingAssembly() + .GetCustomAttribute() + ?.InformationalVersion + ?? "unknown"; + + static string GetOSArch() => + RuntimeInformation.OSArchitecture switch + { + Architecture.X86 => "x32", + Architecture.X64 => "x64", + Architecture.Arm => "arm", + Architecture.Arm64 => "arm64", +#if !NETSTANDARD2_0 + Architecture.Armv6 => "arm64", + Architecture.Wasm + or Architecture.S390x + or Architecture.LoongArch64 + or Architecture.Ppc64le => $"other:{RuntimeInformation.OSArchitecture}", +#endif + _ => "unknown", + }; + + static string GetOS() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return "Windows"; + } + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return "MacOS"; + } + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return "Linux"; + } + return $"Other:{RuntimeInformation.OSDescription}"; + } + + static Runtime GetRuntime() + { + var runtimeDescription = RuntimeInformation.FrameworkDescription; + var lastSpaceIndex = runtimeDescription.LastIndexOf(' '); + if (lastSpaceIndex == -1) + { + return new() { Name = runtimeDescription, Version = "unknown" }; + } + + var name = runtimeDescription.Substring(0, lastSpaceIndex).Trim(); + var version = runtimeDescription.Substring(lastSpaceIndex + 1).Trim(); + return new() + { + Name = name.Length == 0 ? "unknown" : name, + Version = version.Length == 0 ? "unknown" : version, + }; + } + + readonly record struct Runtime + { + public string Name { get; init; } + + public string Version { get; init; } + } +} diff --git a/src/Imagekit/Core/WrappedJsonSerializer.cs b/src/Imagekit/Core/WrappedJsonSerializer.cs new file mode 100644 index 00000000..0650aa66 --- /dev/null +++ b/src/Imagekit/Core/WrappedJsonSerializer.cs @@ -0,0 +1,87 @@ +using System.Text.Json; +using Imagekit.Exceptions; + +namespace Imagekit.Core; + +/// +/// Helper class for deserializing <c>JsonElement</c> objects. This handles +/// edge-cases around nullability and reference/value types. +/// +sealed class WrappedJsonSerializer +{ + public static T GetNotNullClass(JsonElement element, string name) + where T : class + { + T deserialized; + try + { + deserialized = + JsonSerializer.Deserialize(element, ModelBase.SerializerOptions) + ?? throw new ImageKitInvalidDataException($"'{name}' cannot be null"); + } + catch (JsonException e) + { + throw new ImageKitInvalidDataException( + $"'{name}' must be of type {typeof(T).FullName}", + e + ); + } + return deserialized; + } + + public static T GetNotNullStruct(JsonElement element, string name) + where T : struct + { + T deserialized; + try + { + deserialized = + JsonSerializer.Deserialize(element, ModelBase.SerializerOptions) + ?? throw new ImageKitInvalidDataException($"'{name}' cannot be null"); + } + catch (JsonException e) + { + throw new ImageKitInvalidDataException( + $"'{name}' must be of type {typeof(T).FullName}", + e + ); + } + return deserialized; + } + + public static T? GetNullableClass(JsonElement element, string name) + where T : class + { + T? deserialized; + try + { + deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + } + catch (JsonException e) + { + throw new ImageKitInvalidDataException( + $"'{name}' must be of type {typeof(T).FullName}", + e + ); + } + return deserialized; + } + + public static T? GetNullableStruct(JsonElement element, string name) + where T : struct + { + T? deserialized; + try + { + deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + } + catch (JsonException e) + { + throw new ImageKitInvalidDataException( + $"'{name}' must be of type {typeof(T).FullName}", + e + ); + } + return deserialized; + } +} diff --git a/src/Imagekit/Core/WrappedMultipartJsonSerializer.cs b/src/Imagekit/Core/WrappedMultipartJsonSerializer.cs new file mode 100644 index 00000000..8cf6787e --- /dev/null +++ b/src/Imagekit/Core/WrappedMultipartJsonSerializer.cs @@ -0,0 +1,93 @@ +using System.Text.Json; +using Imagekit.Exceptions; + +namespace Imagekit.Core; + +/// +/// Helper class for deserializing <c>MultipartJsonElement</c> objects. +/// This handles edge-cases around nullability and reference/value types. +/// +sealed class WrappedMultipartJsonSerializer +{ + public static T GetNotNullClass(MultipartJsonElement element, string name) + where T : class + { + T deserialized; + try + { + deserialized = + MultipartJsonSerializer.Deserialize(element, ModelBase.SerializerOptions) + ?? throw new ImageKitInvalidDataException($"'{name}' cannot be null"); + } + catch (JsonException e) + { + throw new ImageKitInvalidDataException( + $"'{name}' must be of type {typeof(T).FullName}", + e + ); + } + return deserialized; + } + + public static T GetNotNullStruct(MultipartJsonElement element, string name) + where T : struct + { + T deserialized; + try + { + deserialized = + MultipartJsonSerializer.Deserialize(element, ModelBase.SerializerOptions) + ?? throw new ImageKitInvalidDataException($"'{name}' cannot be null"); + } + catch (JsonException e) + { + throw new ImageKitInvalidDataException( + $"'{name}' must be of type {typeof(T).FullName}", + e + ); + } + return deserialized; + } + + public static T? GetNullableClass(MultipartJsonElement element, string name) + where T : class + { + T? deserialized; + try + { + deserialized = MultipartJsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + } + catch (JsonException e) + { + throw new ImageKitInvalidDataException( + $"'{name}' must be of type {typeof(T).FullName}", + e + ); + } + return deserialized; + } + + public static T? GetNullableStruct(MultipartJsonElement element, string name) + where T : struct + { + T? deserialized; + try + { + deserialized = MultipartJsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + } + catch (JsonException e) + { + throw new ImageKitInvalidDataException( + $"'{name}' must be of type {typeof(T).FullName}", + e + ); + } + return deserialized; + } +} diff --git a/src/Imagekit/Exceptions/ImageKit4xxException.cs b/src/Imagekit/Exceptions/ImageKit4xxException.cs new file mode 100644 index 00000000..fb904eee --- /dev/null +++ b/src/Imagekit/Exceptions/ImageKit4xxException.cs @@ -0,0 +1,9 @@ +using System.Net.Http; + +namespace Imagekit.Exceptions; + +public class ImageKit4xxException : ImageKitApiException +{ + public ImageKit4xxException(HttpRequestException? innerException = null) + : base(innerException) { } +} diff --git a/src/Imagekit/Exceptions/ImageKit5xxException.cs b/src/Imagekit/Exceptions/ImageKit5xxException.cs new file mode 100644 index 00000000..5c3bf57b --- /dev/null +++ b/src/Imagekit/Exceptions/ImageKit5xxException.cs @@ -0,0 +1,9 @@ +using System.Net.Http; + +namespace Imagekit.Exceptions; + +public class ImageKit5xxException : ImageKitApiException +{ + public ImageKit5xxException(HttpRequestException? innerException = null) + : base(innerException) { } +} diff --git a/src/Imagekit/Exceptions/ImageKitApiException.cs b/src/Imagekit/Exceptions/ImageKitApiException.cs new file mode 100644 index 00000000..9ff6b86b --- /dev/null +++ b/src/Imagekit/Exceptions/ImageKitApiException.cs @@ -0,0 +1,35 @@ +using System; +using System.Net; +using System.Net.Http; + +namespace Imagekit.Exceptions; + +public class ImageKitApiException : ImageKitException +{ + public new HttpRequestException InnerException + { + get + { + if (base.InnerException == null) + { + throw new ArgumentNullException(); + } + return (HttpRequestException)base.InnerException; + } + } + + public ImageKitApiException(string message, HttpRequestException? innerException = null) + : base(message, innerException) { } + + protected ImageKitApiException(HttpRequestException? innerException) + : base(innerException) { } + + public required HttpStatusCode StatusCode { get; init; } + + public required string ResponseBody { get; init; } + + public override string Message + { + get { return string.Format("Status Code: {0}\n{1}", StatusCode, ResponseBody); } + } +} diff --git a/src/Imagekit/Exceptions/ImageKitBadRequestException.cs b/src/Imagekit/Exceptions/ImageKitBadRequestException.cs new file mode 100644 index 00000000..5c95d8d4 --- /dev/null +++ b/src/Imagekit/Exceptions/ImageKitBadRequestException.cs @@ -0,0 +1,9 @@ +using System.Net.Http; + +namespace Imagekit.Exceptions; + +public class ImageKitBadRequestException : ImageKit4xxException +{ + public ImageKitBadRequestException(HttpRequestException? innerException = null) + : base(innerException) { } +} diff --git a/src/Imagekit/Exceptions/ImageKitException.cs b/src/Imagekit/Exceptions/ImageKitException.cs new file mode 100644 index 00000000..bf964025 --- /dev/null +++ b/src/Imagekit/Exceptions/ImageKitException.cs @@ -0,0 +1,13 @@ +using System; +using System.Net.Http; + +namespace Imagekit.Exceptions; + +public class ImageKitException : Exception +{ + public ImageKitException(string message, Exception? innerException = null) + : base(message, innerException) { } + + protected ImageKitException(HttpRequestException? innerException) + : base(null, innerException) { } +} diff --git a/src/Imagekit/Exceptions/ImageKitExceptionFactory.cs b/src/Imagekit/Exceptions/ImageKitExceptionFactory.cs new file mode 100644 index 00000000..768f05e5 --- /dev/null +++ b/src/Imagekit/Exceptions/ImageKitExceptionFactory.cs @@ -0,0 +1,61 @@ +using System.Net; + +namespace Imagekit.Exceptions; + +public class ImageKitExceptionFactory +{ + public static ImageKitApiException CreateApiException( + HttpStatusCode statusCode, + string responseBody + ) + { + return (int)statusCode switch + { + 400 => new ImageKitBadRequestException() + { + StatusCode = statusCode, + ResponseBody = responseBody, + }, + 401 => new ImageKitUnauthorizedException() + { + StatusCode = statusCode, + ResponseBody = responseBody, + }, + 403 => new ImageKitForbiddenException() + { + StatusCode = statusCode, + ResponseBody = responseBody, + }, + 404 => new ImageKitNotFoundException() + { + StatusCode = statusCode, + ResponseBody = responseBody, + }, + 422 => new ImageKitUnprocessableEntityException() + { + StatusCode = statusCode, + ResponseBody = responseBody, + }, + 429 => new ImageKitRateLimitException() + { + StatusCode = statusCode, + ResponseBody = responseBody, + }, + >= 400 and <= 499 => new ImageKit4xxException() + { + StatusCode = statusCode, + ResponseBody = responseBody, + }, + >= 500 and <= 599 => new ImageKit5xxException() + { + StatusCode = statusCode, + ResponseBody = responseBody, + }, + _ => new ImageKitUnexpectedStatusCodeException() + { + StatusCode = statusCode, + ResponseBody = responseBody, + }, + }; + } +} diff --git a/src/Imagekit/Exceptions/ImageKitForbiddenException.cs b/src/Imagekit/Exceptions/ImageKitForbiddenException.cs new file mode 100644 index 00000000..fbd39380 --- /dev/null +++ b/src/Imagekit/Exceptions/ImageKitForbiddenException.cs @@ -0,0 +1,9 @@ +using System.Net.Http; + +namespace Imagekit.Exceptions; + +public class ImageKitForbiddenException : ImageKit4xxException +{ + public ImageKitForbiddenException(HttpRequestException? innerException = null) + : base(innerException) { } +} diff --git a/src/Imagekit/Exceptions/ImageKitIOException.cs b/src/Imagekit/Exceptions/ImageKitIOException.cs new file mode 100644 index 00000000..bcfb380a --- /dev/null +++ b/src/Imagekit/Exceptions/ImageKitIOException.cs @@ -0,0 +1,22 @@ +using System; +using System.Net.Http; + +namespace Imagekit.Exceptions; + +public class ImageKitIOException : ImageKitException +{ + public new HttpRequestException InnerException + { + get + { + if (base.InnerException == null) + { + throw new ArgumentNullException(); + } + return (HttpRequestException)base.InnerException; + } + } + + public ImageKitIOException(string message, HttpRequestException? innerException = null) + : base(message, innerException) { } +} diff --git a/src/Imagekit/Exceptions/ImageKitInvalidDataException.cs b/src/Imagekit/Exceptions/ImageKitInvalidDataException.cs new file mode 100644 index 00000000..eda54a08 --- /dev/null +++ b/src/Imagekit/Exceptions/ImageKitInvalidDataException.cs @@ -0,0 +1,9 @@ +using System; + +namespace Imagekit.Exceptions; + +public class ImageKitInvalidDataException : ImageKitException +{ + public ImageKitInvalidDataException(string message, Exception? innerException = null) + : base(message, innerException) { } +} diff --git a/src/Imagekit/Exceptions/ImageKitNotFoundException.cs b/src/Imagekit/Exceptions/ImageKitNotFoundException.cs new file mode 100644 index 00000000..70c81820 --- /dev/null +++ b/src/Imagekit/Exceptions/ImageKitNotFoundException.cs @@ -0,0 +1,9 @@ +using System.Net.Http; + +namespace Imagekit.Exceptions; + +public class ImageKitNotFoundException : ImageKit4xxException +{ + public ImageKitNotFoundException(HttpRequestException? innerException = null) + : base(innerException) { } +} diff --git a/src/Imagekit/Exceptions/ImageKitRateLimitException.cs b/src/Imagekit/Exceptions/ImageKitRateLimitException.cs new file mode 100644 index 00000000..b93fe95a --- /dev/null +++ b/src/Imagekit/Exceptions/ImageKitRateLimitException.cs @@ -0,0 +1,9 @@ +using System.Net.Http; + +namespace Imagekit.Exceptions; + +public class ImageKitRateLimitException : ImageKit4xxException +{ + public ImageKitRateLimitException(HttpRequestException? innerException = null) + : base(innerException) { } +} diff --git a/src/Imagekit/Exceptions/ImageKitUnauthorizedException.cs b/src/Imagekit/Exceptions/ImageKitUnauthorizedException.cs new file mode 100644 index 00000000..0d360cc0 --- /dev/null +++ b/src/Imagekit/Exceptions/ImageKitUnauthorizedException.cs @@ -0,0 +1,9 @@ +using System.Net.Http; + +namespace Imagekit.Exceptions; + +public class ImageKitUnauthorizedException : ImageKit4xxException +{ + public ImageKitUnauthorizedException(HttpRequestException? innerException = null) + : base(innerException) { } +} diff --git a/src/Imagekit/Exceptions/ImageKitUnexpectedStatusCodeException.cs b/src/Imagekit/Exceptions/ImageKitUnexpectedStatusCodeException.cs new file mode 100644 index 00000000..155b0053 --- /dev/null +++ b/src/Imagekit/Exceptions/ImageKitUnexpectedStatusCodeException.cs @@ -0,0 +1,9 @@ +using System.Net.Http; + +namespace Imagekit.Exceptions; + +public class ImageKitUnexpectedStatusCodeException : ImageKitApiException +{ + public ImageKitUnexpectedStatusCodeException(HttpRequestException? innerException = null) + : base(innerException) { } +} diff --git a/src/Imagekit/Exceptions/ImageKitUnprocessableEntityException.cs b/src/Imagekit/Exceptions/ImageKitUnprocessableEntityException.cs new file mode 100644 index 00000000..bc4d6cb8 --- /dev/null +++ b/src/Imagekit/Exceptions/ImageKitUnprocessableEntityException.cs @@ -0,0 +1,9 @@ +using System.Net.Http; + +namespace Imagekit.Exceptions; + +public class ImageKitUnprocessableEntityException : ImageKit4xxException +{ + public ImageKitUnprocessableEntityException(HttpRequestException? innerException = null) + : base(innerException) { } +} diff --git a/src/Imagekit/Helper/AuthenticationParameters.cs b/src/Imagekit/Helper/AuthenticationParameters.cs new file mode 100644 index 00000000..ec3778eb --- /dev/null +++ b/src/Imagekit/Helper/AuthenticationParameters.cs @@ -0,0 +1,16 @@ +namespace Imagekit.Helper; + +/// +/// Authentication parameters for client-side file uploads. +/// +public record AuthenticationParameters +{ + /// A unique token for this upload request. + public required string Token { get; init; } + + /// Unix timestamp (seconds) at which the signature expires. 0 means no expiry. + public required long Expire { get; init; } + + /// HMAC-SHA1 signature of token + expire using the private API key. + public required string Signature { get; init; } +} diff --git a/src/Imagekit/Helper/HelperService.cs b/src/Imagekit/Helper/HelperService.cs new file mode 100644 index 00000000..806a9f24 --- /dev/null +++ b/src/Imagekit/Helper/HelperService.cs @@ -0,0 +1,816 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Security.Cryptography; +using System.Text.RegularExpressions; +using Imagekit.Models; +using SysEncoding = System.Text.Encoding; + +namespace Imagekit.Helper; + +/// +public sealed class HelperService : IHelperService +{ + readonly IImageKitClient _client; + + private static readonly Regex SimpleOverlayPathRegex = new( + @"^[a-zA-Z0-9-._/ ]*$", + RegexOptions.Compiled + ); + private static readonly Regex SimpleOverlayTextRegex = new( + @"^[a-zA-Z0-9-._ ]*$", + RegexOptions.Compiled + ); + private static readonly Regex MultiSlashRegex = new(@"/+", RegexOptions.Compiled); + + private const long DefaultTimestamp = 9999999999L; + + internal HelperService(IImageKitClient client) + { + _client = client; + } + + /// + public AuthenticationParameters GetAuthenticationParameters( + string? token = null, + long? expires = null + ) + { + if (string.IsNullOrEmpty(_client.PrivateKey)) + throw new InvalidOperationException( + "private API key is required for authentication parameters generation" + ); + + string finalToken = token ?? ""; + if (string.IsNullOrEmpty(finalToken)) + { + var bytes = new byte[16]; + using var rng = RandomNumberGenerator.Create(); + rng.GetBytes(bytes); + finalToken = BitConverter.ToString(bytes).Replace("-", "").ToLowerInvariant(); + } + + long finalExpire = expires ?? 0L; + if (finalExpire == 0L) + finalExpire = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + 60 * 30; + + string signature = HmacSha1( + _client.PrivateKey, + finalToken + finalExpire.ToString(CultureInfo.InvariantCulture) + ); + + return new AuthenticationParameters + { + Token = finalToken, + Expire = finalExpire, + Signature = signature, + }; + } + + /// + public string BuildTransformationString(IReadOnlyList transformations) + { + if (transformations == null || transformations.Count == 0) + return ""; + + var steps = new List(); + foreach (var t in transformations) + { + var parts = new List(); + + // Width + if (t.Width != null) + { + if (t.Width.TryPickDouble(out var d)) + parts.Add("w-" + Fmt(d!.Value)); + else if (t.Width.TryPickString(out var s)) + parts.Add("w-" + s); + } + // Height + if (t.Height != null) + { + if (t.Height.TryPickDouble(out var d)) + parts.Add("h-" + Fmt(d!.Value)); + else if (t.Height.TryPickString(out var s)) + parts.Add("h-" + s); + } + // Quality + if (t.Quality != null) + parts.Add("q-" + Fmt(t.Quality.Value)); + // AspectRatio + if (t.AspectRatio != null) + { + if (t.AspectRatio.TryPickDouble(out var d)) + parts.Add("ar-" + Fmt(d!.Value)); + else if (t.AspectRatio.TryPickString(out var s)) + parts.Add("ar-" + s); + } + // Crop + if (t.Crop != null) + parts.Add("c-" + t.Crop.Raw()); + // CropMode + if (t.CropMode != null) + parts.Add("cm-" + t.CropMode.Raw()); + // Focus + if (!string.IsNullOrEmpty(t.Focus)) + parts.Add("fo-" + t.Focus); + // Format + if (t.Format != null) + parts.Add("f-" + t.Format.Raw()); + // Radius + if (t.Radius != null) + { + if (t.Radius.TryPickMax(out _)) + parts.Add("r-max"); + else if (t.Radius.TryPickString(out var s)) + parts.Add("r-" + s); + else if (t.Radius.TryPickDouble(out var d)) + parts.Add("r-" + Fmt(d!.Value)); + } + // Background + if (!string.IsNullOrEmpty(t.Background)) + parts.Add("bg-" + t.Background); + // Border + if (!string.IsNullOrEmpty(t.Border)) + parts.Add("b-" + t.Border); + // ColorReplace + if (!string.IsNullOrEmpty(t.ColorReplace)) + parts.Add("cr-" + t.ColorReplace); + // DefaultImage + if (t.DefaultImage != null) + { + var di = t.DefaultImage.Trim('/'); + if (!string.IsNullOrEmpty(di)) + parts.Add("di-" + di.Replace("/", "@@")); + } + // Dpr + if (t.Dpr != null) + parts.Add("dpr-" + Fmt(t.Dpr.Value)); + // X + if (t.X != null) + { + if (t.X.TryPickDouble(out var d)) + parts.Add("x-" + Fmt(d!.Value)); + else if (t.X.TryPickString(out var s)) + parts.Add("x-" + s); + } + // Y + if (t.Y != null) + { + if (t.Y.TryPickDouble(out var d)) + parts.Add("y-" + Fmt(d!.Value)); + else if (t.Y.TryPickString(out var s)) + parts.Add("y-" + s); + } + // XCenter + if (t.XCenter != null) + { + if (t.XCenter.TryPickDouble(out var d)) + parts.Add("xc-" + Fmt(d!.Value)); + else if (t.XCenter.TryPickString(out var s)) + parts.Add("xc-" + s); + } + // YCenter + if (t.YCenter != null) + { + if (t.YCenter.TryPickDouble(out var d)) + parts.Add("yc-" + Fmt(d!.Value)); + else if (t.YCenter.TryPickString(out var s)) + parts.Add("yc-" + s); + } + // Opacity + if (t.Opacity != null) + parts.Add("o-" + Fmt(t.Opacity.Value)); + // Zoom + if (t.Zoom != null) + parts.Add("z-" + Fmt(t.Zoom.Value)); + // Rotation + if (t.Rotation != null) + { + if (t.Rotation.TryPickDouble(out var d)) + parts.Add("rt-" + Fmt(d!.Value)); + else if (t.Rotation.TryPickString(out var s)) + parts.Add("rt-" + s); + } + // Blur + if (t.Blur != null) + parts.Add("bl-" + Fmt(t.Blur.Value)); + // Named + if (!string.IsNullOrEmpty(t.Named)) + parts.Add("n-" + t.Named); + // Progressive + if (t.Progressive != null) + parts.Add("pr-" + t.Progressive.Value.ToString().ToLowerInvariant()); + // Lossless + if (t.Lossless != null) + parts.Add("lo-" + t.Lossless.Value.ToString().ToLowerInvariant()); + // Flip + if (t.Flip != null) + parts.Add("fl-" + t.Flip.Raw()); + // Trim + if (t.Trim != null) + { + if (t.Trim.TryPickDefault(out _)) + parts.Add("t-true"); + else if (t.Trim.TryPickDouble(out var d)) + parts.Add("t-" + Fmt(d!.Value)); + } + // Metadata + if (t.Metadata != null) + parts.Add("md-" + t.Metadata.Value.ToString().ToLowerInvariant()); + // ColorProfile + if (t.ColorProfile != null) + parts.Add("cp-" + t.ColorProfile.Value.ToString().ToLowerInvariant()); + // VideoCodec + if (t.VideoCodec != null) + parts.Add("vc-" + t.VideoCodec.Raw()); + // AudioCodec + if (t.AudioCodec != null) + parts.Add("ac-" + t.AudioCodec.Raw()); + // StartOffset + if (t.StartOffset != null) + { + if (t.StartOffset.TryPickDouble(out var d)) + parts.Add("so-" + Fmt(d!.Value)); + else if (t.StartOffset.TryPickString(out var s)) + parts.Add("so-" + s); + } + // EndOffset + if (t.EndOffset != null) + { + if (t.EndOffset.TryPickDouble(out var d)) + parts.Add("eo-" + Fmt(d!.Value)); + else if (t.EndOffset.TryPickString(out var s)) + parts.Add("eo-" + s); + } + // Duration + if (t.Duration != null) + { + if (t.Duration.TryPickDouble(out var d)) + parts.Add("du-" + Fmt(d!.Value)); + else if (t.Duration.TryPickString(out var s)) + parts.Add("du-" + s); + } + // StreamingResolutions + if (t.StreamingResolutions != null && t.StreamingResolutions.Count > 0) + parts.Add("sr-" + string.Join("_", t.StreamingResolutions.Select(r => r.Raw()))); + + // AI boolean flags + if (t.Grayscale?.Raw() == true) + parts.Add("e-grayscale"); + if (t.AIUpscale?.Raw() == true) + parts.Add("e-upscale"); + if (t.AIRetouch?.Raw() == true) + parts.Add("e-retouch"); + if (t.AIVariation?.Raw() == true) + parts.Add("e-genvar"); + if (t.AIRemoveBackground?.Raw() == true) + parts.Add("e-bgremove"); + if (t.AIRemoveBackgroundExternal?.Raw() == true) + parts.Add("e-removedotbg"); + if (t.ContrastStretch?.Raw() == true) + parts.Add("e-contrast"); + + // AIDropShadow + if (t.AIDropShadow != null) + { + if (t.AIDropShadow.TryPickDefault(out _)) + parts.Add("e-dropshadow"); + else if (t.AIDropShadow.TryPickString(out var s) && !string.IsNullOrEmpty(s)) + parts.Add("e-dropshadow-" + s); + } + // AIChangeBackground + if (!string.IsNullOrEmpty(t.AIChangeBackground)) + parts.Add("e-changebg-" + t.AIChangeBackground); + // AIEdit + if (!string.IsNullOrEmpty(t.AIEdit)) + parts.Add("e-edit-" + t.AIEdit); + + // Shadow + if (t.Shadow != null) + { + if (t.Shadow.TryPickDefault(out _)) + parts.Add("e-shadow"); + else if (t.Shadow.TryPickString(out var s) && !string.IsNullOrEmpty(s)) + parts.Add("e-shadow-" + s); + } + // Sharpen + if (t.Sharpen != null) + { + if (t.Sharpen.TryPickDefault(out _)) + parts.Add("e-sharpen"); + else if (t.Sharpen.TryPickDouble(out var d)) + parts.Add("e-sharpen-" + Fmt(d!.Value)); + } + // UnsharpMask + if (t.UnsharpMask != null) + { + if (t.UnsharpMask.TryPickDefault(out _)) + parts.Add("e-usm"); + else if (t.UnsharpMask.TryPickString(out var s) && !string.IsNullOrEmpty(s)) + parts.Add("e-usm-" + s); + } + // Gradient + if (t.Gradient != null) + { + if (t.Gradient.TryPickDefault(out _)) + parts.Add("e-gradient"); + else if (t.Gradient.TryPickString(out var s) && !string.IsNullOrEmpty(s)) + parts.Add("e-gradient-" + s); + } + // Colorize + if (!string.IsNullOrEmpty(t.Colorize)) + parts.Add("e-colorize-" + t.Colorize); + // Distort + if (!string.IsNullOrEmpty(t.Distort)) + parts.Add("e-distort-" + t.Distort); + + // Original + if (t.Original == true) + parts.Add("orig-true"); + + // Page + if (t.Page != null) + { + if (t.Page.TryPickDouble(out var d)) + parts.Add("pg-" + Fmt(d!.Value)); + else if (t.Page.TryPickString(out var s)) + parts.Add("pg-" + s); + } + + // Overlay + if (t.Overlay != null) + { + var overlayStr = ProcessOverlay(t.Overlay); + if (!string.IsNullOrEmpty(overlayStr)) + parts.Add(overlayStr); + } + + // Raw (always last) + if (!string.IsNullOrEmpty(t.Raw)) + parts.Add(t.Raw!); + + if (parts.Count > 0) + steps.Add(string.Join(",", parts)); + } + + return string.Join(":", steps); + } + + /// + public string BuildUrl(SrcOptions options) + { + if (string.IsNullOrEmpty(options.Src)) + return ""; + + var src = options.Src; + bool isAbsoluteUrl = + src.StartsWith("http://", StringComparison.OrdinalIgnoreCase) + || src.StartsWith("https://", StringComparison.OrdinalIgnoreCase); + bool isSrcUsedForUrl = isAbsoluteUrl; + + var trPos = options.TransformationPosition?.Value() ?? TransformationPosition.Query; + bool addAsQuery = trPos == TransformationPosition.Query || isSrcUsedForUrl; + + var trStr = BuildTransformationString(options.Transformation ?? new List()); + + ParseUrl( + isAbsoluteUrl ? src : options.UrlEndpoint, + out string scheme, + out string host, + out string basePath, + out string existingQuery + ); + + // Merge query parameters (sorted alphabetically) + var queryParams = new SortedDictionary(StringComparer.Ordinal); + foreach (var kv in ParseQueryString(existingQuery)) + queryParams[kv.Key] = kv.Value; + if (options.QueryParameters != null) + foreach (var kv in options.QueryParameters) + queryParams[kv.Key] = kv.Value; + + // Build path + string finalPath; + if (!isAbsoluteUrl) + { + string encodedSrc = EncodePathSegments(src); + if (!string.IsNullOrEmpty(trStr) && !addAsQuery) + finalPath = NormalizePath(basePath + "/tr:" + trStr + "/" + encodedSrc); + else + finalPath = NormalizePath(basePath + "/" + encodedSrc); + } + else + { + // Absolute URL: basePath comes from Uri.AbsolutePath, already percent-encoded + finalPath = basePath; + } + + string finalUrl = scheme + "://" + host + finalPath; + + // Add sorted query params + if (queryParams.Count > 0) + finalUrl += + "?" + + string.Join( + "&", + queryParams.Select(kv => + Uri.EscapeDataString(kv.Key) + "=" + Uri.EscapeDataString(kv.Value) + ) + ); + + // Add transformation as query param + if (!string.IsNullOrEmpty(trStr) && addAsQuery) + finalUrl += (finalUrl.Contains('?') ? "&" : "?") + "tr=" + trStr; + + // Signing + bool shouldSign = + options.Signed == true || (options.ExpiresIn != null && options.ExpiresIn > 0); + if (shouldSign) + { + long expiryTimestamp = + (options.ExpiresIn != null && options.ExpiresIn > 0) + ? DateTimeOffset.UtcNow.ToUnixTimeSeconds() + (long)options.ExpiresIn.Value + : DefaultTimestamp; + + string signature = GetSignature(finalUrl, options.UrlEndpoint, expiryTimestamp); + string sep = finalUrl.Contains('?') ? "&" : "?"; + if (expiryTimestamp != DefaultTimestamp) + finalUrl += sep + "ik-t=" + expiryTimestamp + "&ik-s=" + signature; + else + finalUrl += sep + "ik-s=" + signature; + } + + return finalUrl; + } + + private string GetSignature(string finalUrl, string urlEndpoint, long expiryTimestamp) + { + if (string.IsNullOrEmpty(_client.PrivateKey)) + return ""; + string endpointWithSlash = urlEndpoint.TrimEnd('/') + "/"; + string relative = finalUrl.StartsWith(endpointWithSlash, StringComparison.Ordinal) + ? finalUrl.Substring(endpointWithSlash.Length) + : finalUrl; + return HmacSha1( + _client.PrivateKey, + relative + expiryTimestamp.ToString(CultureInfo.InvariantCulture) + ); + } + + private static string HmacSha1(string key, string data) + { + using var hmac = new HMACSHA1(SysEncoding.UTF8.GetBytes(key)); + return BitConverter + .ToString(hmac.ComputeHash(SysEncoding.UTF8.GetBytes(data))) + .Replace("-", "") + .ToLowerInvariant(); + } + + private static string Fmt(double value) => value.ToString("G", CultureInfo.InvariantCulture); + + private static void ParseUrl( + string url, + out string scheme, + out string host, + out string path, + out string query + ) + { + var uri = new Uri(url); + scheme = uri.Scheme; + host = uri.IsDefaultPort ? uri.Host : uri.Host + ":" + uri.Port; + path = uri.AbsolutePath; + query = uri.Query.TrimStart('?'); + } + + private static string NormalizePath(string path) => MultiSlashRegex.Replace(path, "/"); + + private static string EncodePathSegments(string path) => + string.Join("/", path.Split('/').Select(Uri.EscapeDataString)); + + private static IEnumerable> ParseQueryString(string query) + { + if (string.IsNullOrEmpty(query)) + yield break; + foreach (var part in query.Split('&')) + { + int eq = part.IndexOf('='); + if (eq < 0) + yield return new KeyValuePair(Uri.UnescapeDataString(part), ""); + else + yield return new KeyValuePair( + Uri.UnescapeDataString(part.Substring(0, eq)), + Uri.UnescapeDataString(part.Substring(eq + 1)) + ); + } + } + + private string ProcessOverlay(Overlay overlay) + { + var entries = new List(); + string trStr = ""; + string? layerMode = null; + OverlayPosition? position = null; + OverlayTiming? timing = null; + + if (overlay.TryPickText(out var textOverlay)) + { + if (string.IsNullOrEmpty(textOverlay!.Text)) + return ""; + var enc = textOverlay.Encoding?.Raw() ?? "auto"; + entries.Add("l-text"); + entries.Add(ProcessText(textOverlay.Text, enc)); + layerMode = textOverlay.LayerMode?.Raw(); + position = textOverlay.Position; + timing = textOverlay.Timing; + if (textOverlay.Transformation?.Count > 0) + trStr = BuildTextOverlayTransformation(textOverlay.Transformation); + } + else if (overlay.TryPickImage(out var imageOverlay)) + { + if (string.IsNullOrEmpty(imageOverlay!.Input)) + return ""; + var enc = imageOverlay.Encoding?.Raw() ?? "auto"; + entries.Add("l-image"); + entries.Add(ProcessInputPath(imageOverlay.Input, enc)); + layerMode = imageOverlay.LayerMode?.Raw(); + position = imageOverlay.Position; + timing = imageOverlay.Timing; + if (imageOverlay.Transformation?.Count > 0) + trStr = BuildTransformationString(imageOverlay.Transformation); + } + else if (overlay.TryPickVideo(out var videoOverlay)) + { + if (string.IsNullOrEmpty(videoOverlay!.Input)) + return ""; + var enc = videoOverlay.Encoding?.Raw() ?? "auto"; + entries.Add("l-video"); + entries.Add(ProcessInputPath(videoOverlay.Input, enc)); + layerMode = videoOverlay.LayerMode?.Raw(); + position = videoOverlay.Position; + timing = videoOverlay.Timing; + if (videoOverlay.Transformation?.Count > 0) + trStr = BuildTransformationString(videoOverlay.Transformation); + } + else if (overlay.TryPickSubtitle(out var subtitleOverlay)) + { + if (string.IsNullOrEmpty(subtitleOverlay!.Input)) + return ""; + var enc = subtitleOverlay.Encoding?.Raw() ?? "auto"; + entries.Add("l-subtitles"); + entries.Add(ProcessInputPath(subtitleOverlay.Input, enc)); + layerMode = subtitleOverlay.LayerMode?.Raw(); + position = subtitleOverlay.Position; + timing = subtitleOverlay.Timing; + if (subtitleOverlay.Transformation?.Count > 0) + trStr = BuildSubtitleOverlayTransformation(subtitleOverlay.Transformation); + } + else if (overlay.TryPickSolidColor(out var solidColorOverlay)) + { + if (string.IsNullOrEmpty(solidColorOverlay!.Color)) + return ""; + entries.Add("l-image"); + entries.Add("i-ik_canvas"); + entries.Add("bg-" + solidColorOverlay.Color); + layerMode = solidColorOverlay.LayerMode?.Raw(); + position = solidColorOverlay.Position; + timing = solidColorOverlay.Timing; + if (solidColorOverlay.Transformation?.Count > 0) + trStr = BuildSolidColorOverlayTransformation(solidColorOverlay.Transformation); + } + + if (entries.Count == 0) + return ""; + + if (!string.IsNullOrEmpty(layerMode)) + entries.Add("lm-" + layerMode); + + if (position != null) + { + if (position.X != null) + { + if (position.X.TryPickDouble(out var d)) + entries.Add("lx-" + Fmt(d!.Value)); + else if (position.X.TryPickString(out var s)) + entries.Add("lx-" + s); + } + if (position.Y != null) + { + if (position.Y.TryPickDouble(out var d)) + entries.Add("ly-" + Fmt(d!.Value)); + else if (position.Y.TryPickString(out var s)) + entries.Add("ly-" + s); + } + if (position.XCenter != null) + { + if (position.XCenter.TryPickDouble(out var d)) + entries.Add("lxc-" + Fmt(d!.Value)); + else if (position.XCenter.TryPickString(out var s)) + entries.Add("lxc-" + s); + } + if (position.YCenter != null) + { + if (position.YCenter.TryPickDouble(out var d)) + entries.Add("lyc-" + Fmt(d!.Value)); + else if (position.YCenter.TryPickString(out var s)) + entries.Add("lyc-" + s); + } + if (position.AnchorPoint != null) + entries.Add("lap-" + position.AnchorPoint.Raw()); + if (position.Focus != null) + entries.Add("lfo-" + position.Focus.Raw()); + } + + if (timing != null) + { + if (timing.Start != null) + { + if (timing.Start.TryPickDouble(out var d)) + entries.Add("lso-" + Fmt(d!.Value)); + else if (timing.Start.TryPickString(out var s)) + entries.Add("lso-" + s); + } + if (timing.End != null) + { + if (timing.End.TryPickDouble(out var d)) + entries.Add("leo-" + Fmt(d!.Value)); + else if (timing.End.TryPickString(out var s)) + entries.Add("leo-" + s); + } + if (timing.Duration != null) + { + if (timing.Duration.TryPickDouble(out var d)) + entries.Add("ldu-" + Fmt(d!.Value)); + else if (timing.Duration.TryPickString(out var s)) + entries.Add("ldu-" + s); + } + } + + if (!string.IsNullOrEmpty(trStr)) + entries.Add(trStr); + entries.Add("l-end"); + return string.Join(",", entries); + } + + private static string BuildTextOverlayTransformation( + IReadOnlyList list + ) + { + var parts = new List(); + foreach (var t in list) + { + if (t.Width != null) + { + if (t.Width.TryPickDouble(out var d)) + parts.Add("w-" + Fmt(d!.Value)); + else if (t.Width.TryPickString(out var s)) + parts.Add("w-" + s); + } + if (t.FontSize != null) + { + if (t.FontSize.TryPickDouble(out var d)) + parts.Add("fs-" + Fmt(d!.Value)); + else if (t.FontSize.TryPickString(out var s)) + parts.Add("fs-" + s); + } + if (!string.IsNullOrEmpty(t.FontFamily)) + parts.Add("ff-" + t.FontFamily!.Trim('/').Replace("/", "@@")); + if (!string.IsNullOrEmpty(t.FontColor)) + parts.Add("co-" + t.FontColor); + if (t.InnerAlignment != null) + parts.Add("ia-" + t.InnerAlignment.Raw()); + if (t.Padding != null) + { + if (t.Padding.TryPickDouble(out var d)) + parts.Add("pa-" + Fmt(d!.Value)); + else if (t.Padding.TryPickString(out var s)) + parts.Add("pa-" + s); + } + if (t.Alpha != null) + parts.Add("al-" + Fmt(t.Alpha.Value)); + if (!string.IsNullOrEmpty(t.Typography)) + parts.Add("tg-" + t.Typography); + if (!string.IsNullOrEmpty(t.Background)) + parts.Add("bg-" + t.Background); + if (t.Radius != null) + { + if (t.Radius.TryPickDouble(out var d)) + parts.Add("r-" + Fmt(d!.Value)); + else if (t.Radius.TryPickMax(out _)) + parts.Add("r-max"); + } + if (t.Rotation != null) + { + if (t.Rotation.TryPickDouble(out var d)) + parts.Add("rt-" + Fmt(d!.Value)); + else if (t.Rotation.TryPickString(out var s)) + parts.Add("rt-" + s); + } + if (t.Flip != null) + parts.Add("fl-" + t.Flip.Raw()); + if (t.LineHeight != null) + { + if (t.LineHeight.TryPickDouble(out var d)) + parts.Add("lh-" + Fmt(d!.Value)); + else if (t.LineHeight.TryPickString(out var s)) + parts.Add("lh-" + s); + } + } + return string.Join(",", parts); + } + + private static string BuildSubtitleOverlayTransformation( + IReadOnlyList list + ) + { + var parts = new List(); + foreach (var t in list) + { + if (!string.IsNullOrEmpty(t.Background)) + parts.Add("bg-" + t.Background); + if (!string.IsNullOrEmpty(t.Color)) + parts.Add("co-" + t.Color); + if (t.FontSize != null) + parts.Add("fs-" + Fmt(t.FontSize.Value)); + if (!string.IsNullOrEmpty(t.FontFamily)) + parts.Add("ff-" + t.FontFamily); + if (!string.IsNullOrEmpty(t.FontOutline)) + parts.Add("fol-" + t.FontOutline); + if (!string.IsNullOrEmpty(t.FontShadow)) + parts.Add("fsh-" + t.FontShadow); + if (t.Typography != null) + parts.Add("tg-" + t.Typography.Raw()); + } + return string.Join(",", parts); + } + + private static string BuildSolidColorOverlayTransformation( + IReadOnlyList list + ) + { + var parts = new List(); + foreach (var t in list) + { + if (t.Width != null) + { + if (t.Width.TryPickDouble(out var d)) + parts.Add("w-" + Fmt(d!.Value)); + else if (t.Width.TryPickString(out var s)) + parts.Add("w-" + s); + } + if (t.Height != null) + { + if (t.Height.TryPickDouble(out var d)) + parts.Add("h-" + Fmt(d!.Value)); + else if (t.Height.TryPickString(out var s)) + parts.Add("h-" + s); + } + if (t.Alpha != null) + parts.Add("al-" + Fmt(t.Alpha.Value)); + if (!string.IsNullOrEmpty(t.Background)) + parts.Add("bg-" + t.Background); + if (t.Gradient != null) + { + if (t.Gradient.TryPickDefault(out _)) + parts.Add("e-gradient"); + else if (t.Gradient.TryPickString(out var s) && !string.IsNullOrEmpty(s)) + parts.Add("e-gradient-" + s); + } + if (t.Radius != null) + { + if (t.Radius.TryPickDouble(out var d)) + parts.Add("r-" + Fmt(d!.Value)); + else if (t.Radius.TryPickMax(out _)) + parts.Add("r-max"); + } + } + return string.Join(",", parts); + } + + private static string ProcessInputPath(string str, string encoding) + { + str = str.Trim('/'); + if (encoding == "plain") + return "i-" + str.Replace("/", "@@"); + if (encoding == "base64") + return "ie-" + + Uri.EscapeDataString(Convert.ToBase64String(SysEncoding.UTF8.GetBytes(str))); + if (SimpleOverlayPathRegex.IsMatch(str)) + return "i-" + str.Replace("/", "@@"); + return "ie-" + Uri.EscapeDataString(Convert.ToBase64String(SysEncoding.UTF8.GetBytes(str))); + } + + private static string ProcessText(string str, string encoding) + { + if (encoding == "plain") + return "i-" + Uri.EscapeDataString(str); + if (encoding == "base64") + return "ie-" + + Uri.EscapeDataString(Convert.ToBase64String(SysEncoding.UTF8.GetBytes(str))); + if (SimpleOverlayTextRegex.IsMatch(str)) + return "i-" + Uri.EscapeDataString(str); + return "ie-" + Uri.EscapeDataString(Convert.ToBase64String(SysEncoding.UTF8.GetBytes(str))); + } +} diff --git a/src/Imagekit/Helper/IHelperService.cs b/src/Imagekit/Helper/IHelperService.cs new file mode 100644 index 00000000..04157d63 --- /dev/null +++ b/src/Imagekit/Helper/IHelperService.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using Imagekit.Models; + +namespace Imagekit.Helper; + +/// +/// Helper utilities for URL generation and authentication. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. +/// +public interface IHelperService +{ + /// + /// Builds an ImageKit delivery URL, optionally applying transformations and signing. + /// + string BuildUrl(SrcOptions options); + + /// + /// Builds a transformation string from a list of transformations. + /// + string BuildTransformationString(IReadOnlyList transformations); + + /// + /// Generates authentication parameters for a client-side file upload. + /// + /// A unique token for this upload request. If null, a UUID is generated. + /// Unix timestamp (seconds) for expiry. If null or 0, defaults to now + 30 minutes. + AuthenticationParameters GetAuthenticationParameters( + string? token = null, + long? expires = null + ); +} diff --git a/src/Imagekit/Helper/UploadHelpers.cs b/src/Imagekit/Helper/UploadHelpers.cs new file mode 100644 index 00000000..a72b6351 --- /dev/null +++ b/src/Imagekit/Helper/UploadHelpers.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using Imagekit.Core; + +namespace Imagekit.Helper; + +internal static class UploadHelpers +{ + const string UploadBaseUrl = "https://upload.imagekit.io"; + + internal static string GetUploadBaseUrl(string configuredBaseUrl) => + string.Equals( + configuredBaseUrl, + EnvironmentUrl.Production, + StringComparison.OrdinalIgnoreCase + ) + ? UploadBaseUrl + : configuredBaseUrl; + + internal static HttpContent SerializeUploadBody( + IReadOnlyDictionary rawData + ) + { + var data = new Dictionary(); + foreach (var kvp in rawData) + data[kvp.Key] = kvp.Value; + + SerializeFieldAsJsonString(data, "extensions"); + SerializeFieldAsJsonString(data, "customMetadata"); + SerializeFieldAsJsonString(data, "transformation"); + + return MultipartJsonSerializer.Serialize(data); + } + + private static void SerializeFieldAsJsonString( + Dictionary data, + string key + ) + { + if (data.TryGetValue(key, out var element)) + data[key] = MultipartJsonSerializer.SerializeToElement(element.Json.GetRawText()); + } +} diff --git a/src/Imagekit/IImageKitClient.cs b/src/Imagekit/IImageKitClient.cs new file mode 100644 index 00000000..8f0e0937 --- /dev/null +++ b/src/Imagekit/IImageKitClient.cs @@ -0,0 +1,169 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Helper; +using Imagekit.Services; + +namespace Imagekit; + +/// +/// A client for interacting with the Image Kit REST API. +/// +/// This client performs best when you create a single instance and reuse it +/// for all interactions with the REST API. This is because each client holds its +/// own connection pool and thread pools. Reusing connections and threads reduces +/// latency and saves memory. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public interface IImageKitClient : IDisposable +{ + /// + HttpClient HttpClient { get; init; } + + /// + string BaseUrl { get; init; } + + /// + bool ResponseValidation { get; init; } + + /// + int? MaxRetries { get; init; } + + /// + TimeSpan? Timeout { get; init; } + + /// + /// Your ImageKit private API key (starts with `private_`). You can find this + /// in the [ImageKit dashboard](https://imagekit.io/dashboard/developer/api-keys). + /// + string PrivateKey { get; init; } + + /// + /// ImageKit uses your API key as username and ignores the password. The SDK + /// sets a dummy value. You can ignore this field. + /// + string? Password { get; init; } + + /// + /// Your ImageKit webhook secret for verifying webhook signatures (starts with + /// `whsec_`). You can find this in the [ImageKit dashboard](https://imagekit.io/dashboard/developer/webhooks). + /// Only required if you're using webhooks. + /// + string? WebhookSecret { get; init; } + + /// + /// Returns a view of this service that provides access to raw HTTP responses + /// for each method. + /// + IImageKitClientWithRawResponse WithRawResponse { get; } + + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IImageKitClient WithOptions(Func modifier); + + IDummyService Dummy { get; } + + ICustomMetadataFieldService CustomMetadataFields { get; } + + IFileService Files { get; } + + ISavedExtensionService SavedExtensions { get; } + + IAssetService Assets { get; } + + ICacheService Cache { get; } + + IFolderService Folders { get; } + + IAccountService Accounts { get; } + + IBetaService Beta { get; } + + IWebhookService Webhooks { get; } + + IHelperService Helper { get; } +} + +/// +/// A view of that provides access to raw HTTP responses for each method. +/// +public interface IImageKitClientWithRawResponse : IDisposable +{ + /// + HttpClient HttpClient { get; init; } + + /// + string BaseUrl { get; init; } + + /// + bool ResponseValidation { get; init; } + + /// + int? MaxRetries { get; init; } + + /// + TimeSpan? Timeout { get; init; } + + /// + /// Your ImageKit private API key (starts with `private_`). You can find this + /// in the [ImageKit dashboard](https://imagekit.io/dashboard/developer/api-keys). + /// + string PrivateKey { get; init; } + + /// + /// ImageKit uses your API key as username and ignores the password. The SDK + /// sets a dummy value. You can ignore this field. + /// + string? Password { get; init; } + + /// + /// Your ImageKit webhook secret for verifying webhook signatures (starts with + /// `whsec_`). You can find this in the [ImageKit dashboard](https://imagekit.io/dashboard/developer/webhooks). + /// Only required if you're using webhooks. + /// + string? WebhookSecret { get; init; } + + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IImageKitClientWithRawResponse WithOptions(Func modifier); + + IDummyServiceWithRawResponse Dummy { get; } + + ICustomMetadataFieldServiceWithRawResponse CustomMetadataFields { get; } + + IFileServiceWithRawResponse Files { get; } + + ISavedExtensionServiceWithRawResponse SavedExtensions { get; } + + IAssetServiceWithRawResponse Assets { get; } + + ICacheServiceWithRawResponse Cache { get; } + + IFolderServiceWithRawResponse Folders { get; } + + IAccountServiceWithRawResponse Accounts { get; } + + IBetaServiceWithRawResponse Beta { get; } + + IWebhookServiceWithRawResponse Webhooks { get; } + + /// + /// Sends a request to the Image Kit REST API. + /// + Task Execute( + HttpRequest request, + CancellationToken cancellationToken = default + ) + where T : ParamsBase; +} diff --git a/src/Imagekit/IPageExtensions.cs b/src/Imagekit/IPageExtensions.cs new file mode 100644 index 00000000..1a8221fc --- /dev/null +++ b/src/Imagekit/IPageExtensions.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading; +using Imagekit.Core; + +namespace Imagekit; + +public static class IPageExtensions +{ + /// + /// Returns a lazy async enumerable that auto-paginates by yielding the given + /// page's data and then requesting and yielding additional pages from the API. + /// + public static async IAsyncEnumerable Paginate( + this IPage page, + [EnumeratorCancellation] CancellationToken cancellationToken = default + ) + { + while (true) + { + foreach (var element in page.Items) + { + yield return element; + } + cancellationToken.ThrowIfCancellationRequested(); + if (!page.HasNext()) + { + break; + } + page = await page.Next(cancellationToken); + } + } +} diff --git a/src/Imagekit/ImageKitClient.cs b/src/Imagekit/ImageKitClient.cs new file mode 100644 index 00000000..2111999c --- /dev/null +++ b/src/Imagekit/ImageKitClient.cs @@ -0,0 +1,538 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Helper; +using Imagekit.Services; + +namespace Imagekit; + +/// +public sealed class ImageKitClient : IImageKitClient +{ + readonly ClientOptions _options; + + /// + public HttpClient HttpClient + { + get { return this._options.HttpClient; } + init { this._options.HttpClient = value; } + } + + /// + public string BaseUrl + { + get { return this._options.BaseUrl; } + init { this._options.BaseUrl = value; } + } + + /// + public bool ResponseValidation + { + get { return this._options.ResponseValidation; } + init { this._options.ResponseValidation = value; } + } + + /// + public int? MaxRetries + { + get { return this._options.MaxRetries; } + init { this._options.MaxRetries = value; } + } + + /// + public TimeSpan? Timeout + { + get { return this._options.Timeout; } + init { this._options.Timeout = value; } + } + + /// + public string PrivateKey + { + get { return this._options.PrivateKey; } + init { this._options.PrivateKey = value; } + } + + /// + public string? Password + { + get { return this._options.Password; } + init { this._options.Password = value; } + } + + /// + public string? WebhookSecret + { + get { return this._options.WebhookSecret; } + init { this._options.WebhookSecret = value; } + } + + readonly Lazy _withRawResponse; + + /// + public IImageKitClientWithRawResponse WithRawResponse + { + get { return _withRawResponse.Value; } + } + + /// + public IImageKitClient WithOptions(Func modifier) + { + return new ImageKitClient(modifier(this._options)); + } + + readonly Lazy _dummy; + public IDummyService Dummy + { + get { return _dummy.Value; } + } + + readonly Lazy _customMetadataFields; + public ICustomMetadataFieldService CustomMetadataFields + { + get { return _customMetadataFields.Value; } + } + + readonly Lazy _files; + public IFileService Files + { + get { return _files.Value; } + } + + readonly Lazy _savedExtensions; + public ISavedExtensionService SavedExtensions + { + get { return _savedExtensions.Value; } + } + + readonly Lazy _assets; + public IAssetService Assets + { + get { return _assets.Value; } + } + + readonly Lazy _cache; + public ICacheService Cache + { + get { return _cache.Value; } + } + + readonly Lazy _folders; + public IFolderService Folders + { + get { return _folders.Value; } + } + + readonly Lazy _accounts; + public IAccountService Accounts + { + get { return _accounts.Value; } + } + + readonly Lazy _beta; + public IBetaService Beta + { + get { return _beta.Value; } + } + + readonly Lazy _webhooks; + public IWebhookService Webhooks + { + get { return _webhooks.Value; } + } + + readonly Lazy _helper; + public IHelperService Helper + { + get { return _helper.Value; } + } + + public void Dispose() => this.HttpClient.Dispose(); + + public ImageKitClient() + { + _options = new(); + + _withRawResponse = new(() => new ImageKitClientWithRawResponse(this._options)); + _dummy = new(() => new DummyService(this)); + _customMetadataFields = new(() => new CustomMetadataFieldService(this)); + _files = new(() => new FileService(this)); + _savedExtensions = new(() => new SavedExtensionService(this)); + _assets = new(() => new AssetService(this)); + _cache = new(() => new CacheService(this)); + _folders = new(() => new FolderService(this)); + _accounts = new(() => new AccountService(this)); + _beta = new(() => new BetaService(this)); + _webhooks = new(() => new WebhookService(this)); + _helper = new(() => new HelperService(this)); + } + + public ImageKitClient(ClientOptions options) + : this() + { + _options = options; + } +} + +/// +public sealed class ImageKitClientWithRawResponse : IImageKitClientWithRawResponse +{ +#if NET + static readonly Random Random = Random.Shared; +#else + static readonly ThreadLocal _threadLocalRandom = new(() => new Random()); + + static Random Random + { + get { return _threadLocalRandom.Value!; } + } +#endif + + internal static HttpMethod PatchMethod = new("PATCH"); + + readonly ClientOptions _options; + + /// + public HttpClient HttpClient + { + get { return this._options.HttpClient; } + init { this._options.HttpClient = value; } + } + + /// + public string BaseUrl + { + get { return this._options.BaseUrl; } + init { this._options.BaseUrl = value; } + } + + /// + public bool ResponseValidation + { + get { return this._options.ResponseValidation; } + init { this._options.ResponseValidation = value; } + } + + /// + public int? MaxRetries + { + get { return this._options.MaxRetries; } + init { this._options.MaxRetries = value; } + } + + /// + public TimeSpan? Timeout + { + get { return this._options.Timeout; } + init { this._options.Timeout = value; } + } + + /// + public string PrivateKey + { + get { return this._options.PrivateKey; } + init { this._options.PrivateKey = value; } + } + + /// + public string? Password + { + get { return this._options.Password; } + init { this._options.Password = value; } + } + + /// + public string? WebhookSecret + { + get { return this._options.WebhookSecret; } + init { this._options.WebhookSecret = value; } + } + + /// + public IImageKitClientWithRawResponse WithOptions(Func modifier) + { + return new ImageKitClientWithRawResponse(modifier(this._options)); + } + + readonly Lazy _dummy; + public IDummyServiceWithRawResponse Dummy + { + get { return _dummy.Value; } + } + + readonly Lazy _customMetadataFields; + public ICustomMetadataFieldServiceWithRawResponse CustomMetadataFields + { + get { return _customMetadataFields.Value; } + } + + readonly Lazy _files; + public IFileServiceWithRawResponse Files + { + get { return _files.Value; } + } + + readonly Lazy _savedExtensions; + public ISavedExtensionServiceWithRawResponse SavedExtensions + { + get { return _savedExtensions.Value; } + } + + readonly Lazy _assets; + public IAssetServiceWithRawResponse Assets + { + get { return _assets.Value; } + } + + readonly Lazy _cache; + public ICacheServiceWithRawResponse Cache + { + get { return _cache.Value; } + } + + readonly Lazy _folders; + public IFolderServiceWithRawResponse Folders + { + get { return _folders.Value; } + } + + readonly Lazy _accounts; + public IAccountServiceWithRawResponse Accounts + { + get { return _accounts.Value; } + } + + readonly Lazy _beta; + public IBetaServiceWithRawResponse Beta + { + get { return _beta.Value; } + } + + readonly Lazy _webhooks; + public IWebhookServiceWithRawResponse Webhooks + { + get { return _webhooks.Value; } + } + + /// + public async Task Execute( + HttpRequest request, + CancellationToken cancellationToken = default + ) + where T : ParamsBase + { + var maxRetries = this.MaxRetries ?? ClientOptions.DefaultMaxRetries; + var retries = 0; + while (true) + { + HttpResponse? response = null; + try + { + response = await ExecuteOnce(request, retries, cancellationToken) + .ConfigureAwait(false); + } + catch (Exception e) + { + if (++retries > maxRetries || !ShouldRetry(e)) + { + throw; + } + } + + if (response != null && (++retries > maxRetries || !ShouldRetry(response))) + { + if (response.IsSuccessStatusCode) + { + return response; + } + + try + { + throw ImageKitExceptionFactory.CreateApiException( + response.StatusCode, + await response.ReadAsString(cancellationToken).ConfigureAwait(false) + ); + } + catch (HttpRequestException e) + { + throw new ImageKitIOException("I/O Exception", e); + } + finally + { + response.Dispose(); + } + } + + var backoff = ComputeRetryBackoff(retries, response); + response?.Dispose(); + await Task.Delay(backoff, cancellationToken).ConfigureAwait(false); + } + } + + async Task ExecuteOnce( + HttpRequest request, + int retryCount, + CancellationToken cancellationToken = default + ) + where T : ParamsBase + { + using HttpRequestMessage requestMessage = new( + request.Method, + request.Params.Url(this._options) + ) + { + Content = request.Params.BodyContent(), + }; + request.Params.AddHeadersToRequest(requestMessage, this._options); + if (!requestMessage.Headers.Contains("x-stainless-retry-count")) + { + requestMessage.Headers.Add("x-stainless-retry-count", retryCount.ToString()); + } + using CancellationTokenSource timeoutCts = new( + this.Timeout ?? ClientOptions.DefaultTimeout + ); + using var cts = CancellationTokenSource.CreateLinkedTokenSource( + timeoutCts.Token, + cancellationToken + ); + HttpResponseMessage responseMessage; + try + { + responseMessage = await this + .HttpClient.SendAsync( + requestMessage, + HttpCompletionOption.ResponseHeadersRead, + cts.Token + ) + .ConfigureAwait(false); + } + catch (HttpRequestException e) + { + throw new ImageKitIOException("I/O exception", e); + } + return new() { RawMessage = responseMessage, CancellationToken = cts.Token }; + } + + static TimeSpan ComputeRetryBackoff(int retries, HttpResponse? response) + { + TimeSpan? apiBackoff = ParseRetryAfterMsHeader(response) ?? ParseRetryAfterHeader(response); + if ( + apiBackoff != null + && apiBackoff > TimeSpan.Zero + && apiBackoff < TimeSpan.FromMinutes(1) + ) + { + // If the API asks us to wait a certain amount of time (and it's a reasonable amount), then just + // do what it says. + return (TimeSpan)apiBackoff; + } + + // Apply exponential backoff, but not more than the max. + var backoffSeconds = Math.Min(0.5 * Math.Pow(2.0, retries - 1), 8.0); + var jitter = 1.0 - 0.25 * Random.NextDouble(); + return TimeSpan.FromSeconds(backoffSeconds * jitter); + } + + static TimeSpan? ParseRetryAfterMsHeader(HttpResponse? response) + { + IEnumerable? headerValues = null; + response?.TryGetHeaderValues("Retry-After-Ms", out headerValues); + var headerValue = headerValues == null ? null : Enumerable.FirstOrDefault(headerValues); + if (headerValue == null) + { + return null; + } + + if (float.TryParse(headerValue, out var retryAfterMs)) + { + return TimeSpan.FromMilliseconds(retryAfterMs); + } + + return null; + } + + static TimeSpan? ParseRetryAfterHeader(HttpResponse? response) + { + IEnumerable? headerValues = null; + response?.TryGetHeaderValues("Retry-After", out headerValues); + var headerValue = headerValues == null ? null : Enumerable.FirstOrDefault(headerValues); + if (headerValue == null) + { + return null; + } + + if (float.TryParse(headerValue, out var retryAfterSeconds)) + { + return TimeSpan.FromSeconds(retryAfterSeconds); + } + else if (DateTimeOffset.TryParse(headerValue, out var retryAfterDate)) + { + return retryAfterDate - DateTimeOffset.Now; + } + + return null; + } + + static bool ShouldRetry(HttpResponse response) + { + if ( + response.TryGetHeaderValues("X-Should-Retry", out var headerValues) + && bool.TryParse(Enumerable.FirstOrDefault(headerValues), out var shouldRetry) + ) + { + // If the server explicitly says whether to retry, then we obey. + return shouldRetry; + } + + return (int)response.StatusCode switch + { + // Retry on request timeouts + 408 + or + // Retry on lock timeouts + 409 + or + // Retry on rate limits + 429 + or + // Retry internal errors + >= 500 => true, + _ => false, + }; + } + + static bool ShouldRetry(Exception e) + { + return e is IOException || e is ImageKitIOException; + } + + public void Dispose() => this.HttpClient.Dispose(); + + public ImageKitClientWithRawResponse() + { + _options = new(); + + _dummy = new(() => new DummyServiceWithRawResponse(this)); + _customMetadataFields = new(() => new CustomMetadataFieldServiceWithRawResponse(this)); + _files = new(() => new FileServiceWithRawResponse(this)); + _savedExtensions = new(() => new SavedExtensionServiceWithRawResponse(this)); + _assets = new(() => new AssetServiceWithRawResponse(this)); + _cache = new(() => new CacheServiceWithRawResponse(this)); + _folders = new(() => new FolderServiceWithRawResponse(this)); + _accounts = new(() => new AccountServiceWithRawResponse(this)); + _beta = new(() => new BetaServiceWithRawResponse(this)); + _webhooks = new(() => new WebhookServiceWithRawResponse(this)); + } + + public ImageKitClientWithRawResponse(ClientOptions options) + : this() + { + _options = options; + } +} diff --git a/src/Imagekit/Imagekit.csproj b/src/Imagekit/Imagekit.csproj new file mode 100644 index 00000000..d5d5a7c7 --- /dev/null +++ b/src/Imagekit/Imagekit.csproj @@ -0,0 +1,20 @@ + + + + Image Kit C# + Imagekit + 6.0.0 + The official .NET library for the Image Kit API. + Library + README.md + + + + + + + + + + + \ No newline at end of file diff --git a/src/Imagekit/Models/AITag.cs b/src/Imagekit/Models/AITag.cs new file mode 100644 index 00000000..a733d548 --- /dev/null +++ b/src/Imagekit/Models/AITag.cs @@ -0,0 +1,121 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models; + +/// +/// AI-generated tag associated with an image. These tags can be added using the `google-auto-tagging` +/// or `aws-auto-tagging` extensions. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class AITag : JsonModel +{ + /// + /// Confidence score of the tag. + /// + public double? Confidence + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("confidence"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("confidence", value); + } + } + + /// + /// Name of the tag. + /// + public string? Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("name"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("name", value); + } + } + + /// + /// Source of the tag. Possible values are `google-auto-tagging` and `aws-auto-tagging`. + /// + public string? Source + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("source"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("source", value); + } + } + + /// + public override void Validate() + { + _ = this.Confidence; + _ = this.Name; + _ = this.Source; + } + + public AITag() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public AITag(AITag aiTag) + : base(aiTag) { } +#pragma warning restore CS8618 + + public AITag(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + AITag(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static AITag FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class AITagFromRaw : IFromRawJson +{ + /// + public AITag FromRawUnchecked(IReadOnlyDictionary rawData) => + AITag.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Accounts/Origins/OriginCreateParams.cs b/src/Imagekit/Models/Accounts/Origins/OriginCreateParams.cs new file mode 100644 index 00000000..602166ad --- /dev/null +++ b/src/Imagekit/Models/Accounts/Origins/OriginCreateParams.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; +using Text = System.Text; + +namespace Imagekit.Models.Accounts.Origins; + +/// +/// **Note:** This API is currently in beta. Creates a new origin and returns the +/// origin object. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class OriginCreateParams : ParamsBase +{ + public JsonElement RawBodyData { get; private init; } + + /// + /// Schema for origin request resources. + /// + public required OriginRequest OriginRequest + { + get + { + return WrappedJsonSerializer.GetNotNullClass( + this.RawBodyData, + "RawBodyData" + ); + } + init { this.RawBodyData = JsonSerializer.SerializeToElement(value); } + } + + public OriginCreateParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public OriginCreateParams(OriginCreateParams originCreateParams) + : base(originCreateParams) + { + this.RawBodyData = originCreateParams.RawBodyData; + } +#pragma warning restore CS8618 + + public OriginCreateParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + JsonElement rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this.RawBodyData = rawBodyData; + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + OriginCreateParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + JsonElement rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this.RawBodyData = rawBodyData; + } +#pragma warning restore CS8618 + + /// + public static OriginCreateParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + JsonElement rawBodyData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + rawBodyData + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this.RawBodyData), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(OriginCreateParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this.RawBodyData.Equals(other.RawBodyData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder(options.BaseUrl.ToString().TrimEnd('/') + "/v1/accounts/origins") + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Accounts/Origins/OriginDeleteParams.cs b/src/Imagekit/Models/Accounts/Origins/OriginDeleteParams.cs new file mode 100644 index 00000000..f7320bc0 --- /dev/null +++ b/src/Imagekit/Models/Accounts/Origins/OriginDeleteParams.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Models.Accounts.Origins; + +/// +/// **Note:** This API is currently in beta. Permanently removes the origin identified +/// by `id`. If the origin is in use by any URL‑endpoints, the API will return an error. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class OriginDeleteParams : ParamsBase +{ + public string? ID { get; init; } + + public OriginDeleteParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public OriginDeleteParams(OriginDeleteParams originDeleteParams) + : base(originDeleteParams) + { + this.ID = originDeleteParams.ID; + } +#pragma warning restore CS8618 + + public OriginDeleteParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + OriginDeleteParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + string id + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this.ID = id; + } +#pragma warning restore CS8618 + + /// + public static OriginDeleteParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + string id + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + id + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["ID"] = JsonSerializer.SerializeToElement(this.ID), + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(OriginDeleteParams? other) + { + if (other == null) + { + return false; + } + return (this.ID?.Equals(other.ID) ?? other.ID == null) + && this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + + string.Format("/v1/accounts/origins/{0}", this.ID) + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Accounts/Origins/OriginGetParams.cs b/src/Imagekit/Models/Accounts/Origins/OriginGetParams.cs new file mode 100644 index 00000000..25f16ec4 --- /dev/null +++ b/src/Imagekit/Models/Accounts/Origins/OriginGetParams.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Models.Accounts.Origins; + +/// +/// **Note:** This API is currently in beta. Retrieves the origin identified by `id`. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class OriginGetParams : ParamsBase +{ + public string? ID { get; init; } + + public OriginGetParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public OriginGetParams(OriginGetParams originGetParams) + : base(originGetParams) + { + this.ID = originGetParams.ID; + } +#pragma warning restore CS8618 + + public OriginGetParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + OriginGetParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + string id + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this.ID = id; + } +#pragma warning restore CS8618 + + /// + public static OriginGetParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + string id + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + id + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["ID"] = JsonSerializer.SerializeToElement(this.ID), + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(OriginGetParams? other) + { + if (other == null) + { + return false; + } + return (this.ID?.Equals(other.ID) ?? other.ID == null) + && this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + + string.Format("/v1/accounts/origins/{0}", this.ID) + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Accounts/Origins/OriginListParams.cs b/src/Imagekit/Models/Accounts/Origins/OriginListParams.cs new file mode 100644 index 00000000..96cf9a55 --- /dev/null +++ b/src/Imagekit/Models/Accounts/Origins/OriginListParams.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Models.Accounts.Origins; + +/// +/// **Note:** This API is currently in beta. Returns an array of all configured +/// origins for the current account. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class OriginListParams : ParamsBase +{ + public OriginListParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public OriginListParams(OriginListParams originListParams) + : base(originListParams) { } +#pragma warning restore CS8618 + + public OriginListParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + OriginListParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } +#pragma warning restore CS8618 + + /// + public static OriginListParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(OriginListParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder(options.BaseUrl.ToString().TrimEnd('/') + "/v1/accounts/origins") + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Accounts/Origins/OriginRequest.cs b/src/Imagekit/Models/Accounts/Origins/OriginRequest.cs new file mode 100644 index 00000000..c1b6bc07 --- /dev/null +++ b/src/Imagekit/Models/Accounts/Origins/OriginRequest.cs @@ -0,0 +1,2197 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models.Accounts.Origins; + +/// +/// Schema for origin request resources. +/// +[JsonConverter(typeof(OriginRequestConverter))] +public record class OriginRequest : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public string? AccessKey + { + get + { + return Match( + s3: (x) => x.AccessKey, + s3Compatible: (x) => x.AccessKey, + cloudinaryBackup: (x) => x.AccessKey, + webFolder: (_) => null, + webProxy: (_) => null, + gcs: (_) => null, + azureBlob: (_) => null, + akeneoPim: (_) => null + ); + } + } + + public string? Bucket + { + get + { + return Match( + s3: (x) => x.Bucket, + s3Compatible: (x) => x.Bucket, + cloudinaryBackup: (x) => x.Bucket, + webFolder: (_) => null, + webProxy: (_) => null, + gcs: (x) => x.Bucket, + azureBlob: (_) => null, + akeneoPim: (_) => null + ); + } + } + + public string Name + { + get + { + return Match( + s3: (x) => x.Name, + s3Compatible: (x) => x.Name, + cloudinaryBackup: (x) => x.Name, + webFolder: (x) => x.Name, + webProxy: (x) => x.Name, + gcs: (x) => x.Name, + azureBlob: (x) => x.Name, + akeneoPim: (x) => x.Name + ); + } + } + + public string? SecretKey + { + get + { + return Match( + s3: (x) => x.SecretKey, + s3Compatible: (x) => x.SecretKey, + cloudinaryBackup: (x) => x.SecretKey, + webFolder: (_) => null, + webProxy: (_) => null, + gcs: (_) => null, + azureBlob: (_) => null, + akeneoPim: (_) => null + ); + } + } + + public JsonElement Type + { + get + { + return Match( + s3: (x) => x.Type, + s3Compatible: (x) => x.Type, + cloudinaryBackup: (x) => x.Type, + webFolder: (x) => x.Type, + webProxy: (x) => x.Type, + gcs: (x) => x.Type, + azureBlob: (x) => x.Type, + akeneoPim: (x) => x.Type + ); + } + } + + public string? BaseUrlForCanonicalHeader + { + get + { + return Match( + s3: (x) => x.BaseUrlForCanonicalHeader, + s3Compatible: (x) => x.BaseUrlForCanonicalHeader, + cloudinaryBackup: (x) => x.BaseUrlForCanonicalHeader, + webFolder: (x) => x.BaseUrlForCanonicalHeader, + webProxy: (x) => x.BaseUrlForCanonicalHeader, + gcs: (x) => x.BaseUrlForCanonicalHeader, + azureBlob: (x) => x.BaseUrlForCanonicalHeader, + akeneoPim: (x) => x.BaseUrlForCanonicalHeader + ); + } + } + + public bool? IncludeCanonicalHeader + { + get + { + return Match( + s3: (x) => x.IncludeCanonicalHeader, + s3Compatible: (x) => x.IncludeCanonicalHeader, + cloudinaryBackup: (x) => x.IncludeCanonicalHeader, + webFolder: (x) => x.IncludeCanonicalHeader, + webProxy: (x) => x.IncludeCanonicalHeader, + gcs: (x) => x.IncludeCanonicalHeader, + azureBlob: (x) => x.IncludeCanonicalHeader, + akeneoPim: (x) => x.IncludeCanonicalHeader + ); + } + } + + public string? Prefix + { + get + { + return Match( + s3: (x) => x.Prefix, + s3Compatible: (x) => x.Prefix, + cloudinaryBackup: (x) => x.Prefix, + webFolder: (_) => null, + webProxy: (_) => null, + gcs: (x) => x.Prefix, + azureBlob: (x) => x.Prefix, + akeneoPim: (_) => null + ); + } + } + + public string? BaseUrl + { + get + { + return Match( + s3: (_) => null, + s3Compatible: (_) => null, + cloudinaryBackup: (_) => null, + webFolder: (x) => x.BaseUrl, + webProxy: (_) => null, + gcs: (_) => null, + azureBlob: (_) => null, + akeneoPim: (x) => x.BaseUrl + ); + } + } + + public OriginRequest(S3 value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OriginRequest(S3Compatible value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OriginRequest(CloudinaryBackup value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OriginRequest(WebFolder value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OriginRequest(WebProxy value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OriginRequest(Gcs value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OriginRequest(AzureBlob value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OriginRequest(AkeneoPim value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OriginRequest(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickS3(out var value)) { + /// // `value` is of type `S3` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickS3([NotNullWhen(true)] out S3? value) + { + value = this.Value as S3; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickS3Compatible(out var value)) { + /// // `value` is of type `S3Compatible` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickS3Compatible([NotNullWhen(true)] out S3Compatible? value) + { + value = this.Value as S3Compatible; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickCloudinaryBackup(out var value)) { + /// // `value` is of type `CloudinaryBackup` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickCloudinaryBackup([NotNullWhen(true)] out CloudinaryBackup? value) + { + value = this.Value as CloudinaryBackup; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickWebFolder(out var value)) { + /// // `value` is of type `WebFolder` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickWebFolder([NotNullWhen(true)] out WebFolder? value) + { + value = this.Value as WebFolder; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickWebProxy(out var value)) { + /// // `value` is of type `WebProxy` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickWebProxy([NotNullWhen(true)] out WebProxy? value) + { + value = this.Value as WebProxy; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickGcs(out var value)) { + /// // `value` is of type `Gcs` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickGcs([NotNullWhen(true)] out Gcs? value) + { + value = this.Value as Gcs; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickAzureBlob(out var value)) { + /// // `value` is of type `AzureBlob` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickAzureBlob([NotNullWhen(true)] out AzureBlob? value) + { + value = this.Value as AzureBlob; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickAkeneoPim(out var value)) { + /// // `value` is of type `AkeneoPim` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickAkeneoPim([NotNullWhen(true)] out AkeneoPim? value) + { + value = this.Value as AkeneoPim; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (S3 value) => {...}, + /// (S3Compatible value) => {...}, + /// (CloudinaryBackup value) => {...}, + /// (WebFolder value) => {...}, + /// (WebProxy value) => {...}, + /// (Gcs value) => {...}, + /// (AzureBlob value) => {...}, + /// (AkeneoPim value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action s3, + System::Action s3Compatible, + System::Action cloudinaryBackup, + System::Action webFolder, + System::Action webProxy, + System::Action gcs, + System::Action azureBlob, + System::Action akeneoPim + ) + { + switch (this.Value) + { + case S3 value: + s3(value); + break; + case S3Compatible value: + s3Compatible(value); + break; + case CloudinaryBackup value: + cloudinaryBackup(value); + break; + case WebFolder value: + webFolder(value); + break; + case WebProxy value: + webProxy(value); + break; + case Gcs value: + gcs(value); + break; + case AzureBlob value: + azureBlob(value); + break; + case AkeneoPim value: + akeneoPim(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of OriginRequest" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (S3 value) => {...}, + /// (S3Compatible value) => {...}, + /// (CloudinaryBackup value) => {...}, + /// (WebFolder value) => {...}, + /// (WebProxy value) => {...}, + /// (Gcs value) => {...}, + /// (AzureBlob value) => {...}, + /// (AkeneoPim value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func s3, + System::Func s3Compatible, + System::Func cloudinaryBackup, + System::Func webFolder, + System::Func webProxy, + System::Func gcs, + System::Func azureBlob, + System::Func akeneoPim + ) + { + return this.Value switch + { + S3 value => s3(value), + S3Compatible value => s3Compatible(value), + CloudinaryBackup value => cloudinaryBackup(value), + WebFolder value => webFolder(value), + WebProxy value => webProxy(value), + Gcs value => gcs(value), + AzureBlob value => azureBlob(value), + AkeneoPim value => akeneoPim(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of OriginRequest" + ), + }; + } + + public static implicit operator OriginRequest(S3 value) => new(value); + + public static implicit operator OriginRequest(S3Compatible value) => new(value); + + public static implicit operator OriginRequest(CloudinaryBackup value) => new(value); + + public static implicit operator OriginRequest(WebFolder value) => new(value); + + public static implicit operator OriginRequest(WebProxy value) => new(value); + + public static implicit operator OriginRequest(Gcs value) => new(value); + + public static implicit operator OriginRequest(AzureBlob value) => new(value); + + public static implicit operator OriginRequest(AkeneoPim value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of OriginRequest" + ); + } + this.Switch( + (s3) => s3.Validate(), + (s3Compatible) => s3Compatible.Validate(), + (cloudinaryBackup) => cloudinaryBackup.Validate(), + (webFolder) => webFolder.Validate(), + (webProxy) => webProxy.Validate(), + (gcs) => gcs.Validate(), + (azureBlob) => azureBlob.Validate(), + (akeneoPim) => akeneoPim.Validate() + ); + } + + public virtual bool Equals(OriginRequest? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + S3 _ => 0, + S3Compatible _ => 1, + CloudinaryBackup _ => 2, + WebFolder _ => 3, + WebProxy _ => 4, + Gcs _ => 5, + AzureBlob _ => 6, + AkeneoPim _ => 7, + _ => -1, + }; + } +} + +sealed class OriginRequestConverter : JsonConverter +{ + public override OriginRequest? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + string? type; + try + { + type = element.GetProperty("type").GetString(); + } + catch + { + type = null; + } + + switch (type) + { + case "S3": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "S3_COMPATIBLE": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "CLOUDINARY_BACKUP": + { + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "WEB_FOLDER": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "WEB_PROXY": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "GCS": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "AZURE_BLOB": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "AKENEO_PIM": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + default: + { + return new OriginRequest(element); + } + } + } + + public override void Write( + Utf8JsonWriter writer, + OriginRequest value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class S3 : JsonModel +{ + /// + /// Access key for the bucket. + /// + public required string AccessKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("accessKey"); + } + init { this._rawData.Set("accessKey", value); } + } + + /// + /// S3 bucket name. + /// + public required string Bucket + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("bucket"); + } + init { this._rawData.Set("bucket", value); } + } + + /// + /// Display name of the origin. + /// + public required string Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("name"); + } + init { this._rawData.Set("name", value); } + } + + /// + /// Secret key for the bucket. + /// + public required string SecretKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("secretKey"); + } + init { this._rawData.Set("secretKey", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// URL used in the Canonical header (if enabled). + /// + public string? BaseUrlForCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseUrlForCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseUrlForCanonicalHeader", value); + } + } + + /// + /// Whether to send a Canonical header. + /// + public bool? IncludeCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("includeCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("includeCanonicalHeader", value); + } + } + + /// + /// Path prefix inside the bucket. + /// + public string? Prefix + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("prefix"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("prefix", value); + } + } + + /// + public override void Validate() + { + _ = this.AccessKey; + _ = this.Bucket; + _ = this.Name; + _ = this.SecretKey; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("S3"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.BaseUrlForCanonicalHeader; + _ = this.IncludeCanonicalHeader; + _ = this.Prefix; + } + + public S3() + { + this.Type = JsonSerializer.SerializeToElement("S3"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public S3(S3 s3) + : base(s3) { } +#pragma warning restore CS8618 + + public S3(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("S3"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + S3(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static S3 FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class S3FromRaw : IFromRawJson +{ + /// + public S3 FromRawUnchecked(IReadOnlyDictionary rawData) => + S3.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class S3Compatible : JsonModel +{ + /// + /// Access key for the bucket. + /// + public required string AccessKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("accessKey"); + } + init { this._rawData.Set("accessKey", value); } + } + + /// + /// S3 bucket name. + /// + public required string Bucket + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("bucket"); + } + init { this._rawData.Set("bucket", value); } + } + + /// + /// Custom S3-compatible endpoint. + /// + public required string Endpoint + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("endpoint"); + } + init { this._rawData.Set("endpoint", value); } + } + + /// + /// Display name of the origin. + /// + public required string Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("name"); + } + init { this._rawData.Set("name", value); } + } + + /// + /// Secret key for the bucket. + /// + public required string SecretKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("secretKey"); + } + init { this._rawData.Set("secretKey", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// URL used in the Canonical header (if enabled). + /// + public string? BaseUrlForCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseUrlForCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseUrlForCanonicalHeader", value); + } + } + + /// + /// Whether to send a Canonical header. + /// + public bool? IncludeCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("includeCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("includeCanonicalHeader", value); + } + } + + /// + /// Path prefix inside the bucket. + /// + public string? Prefix + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("prefix"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("prefix", value); + } + } + + /// + /// Use path-style S3 URLs? + /// + public bool? S3ForcePathStyle + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("s3ForcePathStyle"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("s3ForcePathStyle", value); + } + } + + /// + public override void Validate() + { + _ = this.AccessKey; + _ = this.Bucket; + _ = this.Endpoint; + _ = this.Name; + _ = this.SecretKey; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("S3_COMPATIBLE"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.BaseUrlForCanonicalHeader; + _ = this.IncludeCanonicalHeader; + _ = this.Prefix; + _ = this.S3ForcePathStyle; + } + + public S3Compatible() + { + this.Type = JsonSerializer.SerializeToElement("S3_COMPATIBLE"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public S3Compatible(S3Compatible s3Compatible) + : base(s3Compatible) { } +#pragma warning restore CS8618 + + public S3Compatible(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("S3_COMPATIBLE"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + S3Compatible(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static S3Compatible FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class S3CompatibleFromRaw : IFromRawJson +{ + /// + public S3Compatible FromRawUnchecked(IReadOnlyDictionary rawData) => + S3Compatible.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class CloudinaryBackup : JsonModel +{ + /// + /// Access key for the bucket. + /// + public required string AccessKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("accessKey"); + } + init { this._rawData.Set("accessKey", value); } + } + + /// + /// S3 bucket name. + /// + public required string Bucket + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("bucket"); + } + init { this._rawData.Set("bucket", value); } + } + + /// + /// Display name of the origin. + /// + public required string Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("name"); + } + init { this._rawData.Set("name", value); } + } + + /// + /// Secret key for the bucket. + /// + public required string SecretKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("secretKey"); + } + init { this._rawData.Set("secretKey", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// URL used in the Canonical header (if enabled). + /// + public string? BaseUrlForCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseUrlForCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseUrlForCanonicalHeader", value); + } + } + + /// + /// Whether to send a Canonical header. + /// + public bool? IncludeCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("includeCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("includeCanonicalHeader", value); + } + } + + /// + /// Path prefix inside the bucket. + /// + public string? Prefix + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("prefix"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("prefix", value); + } + } + + /// + public override void Validate() + { + _ = this.AccessKey; + _ = this.Bucket; + _ = this.Name; + _ = this.SecretKey; + if ( + !JsonElement.DeepEquals( + this.Type, + JsonSerializer.SerializeToElement("CLOUDINARY_BACKUP") + ) + ) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.BaseUrlForCanonicalHeader; + _ = this.IncludeCanonicalHeader; + _ = this.Prefix; + } + + public CloudinaryBackup() + { + this.Type = JsonSerializer.SerializeToElement("CLOUDINARY_BACKUP"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public CloudinaryBackup(CloudinaryBackup cloudinaryBackup) + : base(cloudinaryBackup) { } +#pragma warning restore CS8618 + + public CloudinaryBackup(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("CLOUDINARY_BACKUP"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + CloudinaryBackup(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static CloudinaryBackup FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class CloudinaryBackupFromRaw : IFromRawJson +{ + /// + public CloudinaryBackup FromRawUnchecked(IReadOnlyDictionary rawData) => + CloudinaryBackup.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class WebFolder : JsonModel +{ + /// + /// Root URL for the web folder origin. + /// + public required string BaseUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("baseUrl"); + } + init { this._rawData.Set("baseUrl", value); } + } + + /// + /// Display name of the origin. + /// + public required string Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("name"); + } + init { this._rawData.Set("name", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// URL used in the Canonical header (if enabled). + /// + public string? BaseUrlForCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseUrlForCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseUrlForCanonicalHeader", value); + } + } + + /// + /// Forward the Host header to origin? + /// + public bool? ForwardHostHeaderToOrigin + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("forwardHostHeaderToOrigin"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("forwardHostHeaderToOrigin", value); + } + } + + /// + /// Whether to send a Canonical header. + /// + public bool? IncludeCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("includeCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("includeCanonicalHeader", value); + } + } + + /// + public override void Validate() + { + _ = this.BaseUrl; + _ = this.Name; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("WEB_FOLDER"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.BaseUrlForCanonicalHeader; + _ = this.ForwardHostHeaderToOrigin; + _ = this.IncludeCanonicalHeader; + } + + public WebFolder() + { + this.Type = JsonSerializer.SerializeToElement("WEB_FOLDER"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public WebFolder(WebFolder webFolder) + : base(webFolder) { } +#pragma warning restore CS8618 + + public WebFolder(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("WEB_FOLDER"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + WebFolder(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static WebFolder FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class WebFolderFromRaw : IFromRawJson +{ + /// + public WebFolder FromRawUnchecked(IReadOnlyDictionary rawData) => + WebFolder.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class WebProxy : JsonModel +{ + /// + /// Display name of the origin. + /// + public required string Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("name"); + } + init { this._rawData.Set("name", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// URL used in the Canonical header (if enabled). + /// + public string? BaseUrlForCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseUrlForCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseUrlForCanonicalHeader", value); + } + } + + /// + /// Whether to send a Canonical header. + /// + public bool? IncludeCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("includeCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("includeCanonicalHeader", value); + } + } + + /// + public override void Validate() + { + _ = this.Name; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("WEB_PROXY"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.BaseUrlForCanonicalHeader; + _ = this.IncludeCanonicalHeader; + } + + public WebProxy() + { + this.Type = JsonSerializer.SerializeToElement("WEB_PROXY"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public WebProxy(WebProxy webProxy) + : base(webProxy) { } +#pragma warning restore CS8618 + + public WebProxy(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("WEB_PROXY"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + WebProxy(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static WebProxy FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public WebProxy(string name) + : this() + { + this.Name = name; + } +} + +class WebProxyFromRaw : IFromRawJson +{ + /// + public WebProxy FromRawUnchecked(IReadOnlyDictionary rawData) => + WebProxy.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Gcs : JsonModel +{ + public required string Bucket + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("bucket"); + } + init { this._rawData.Set("bucket", value); } + } + + public required string ClientEmail + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("clientEmail"); + } + init { this._rawData.Set("clientEmail", value); } + } + + /// + /// Display name of the origin. + /// + public required string Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("name"); + } + init { this._rawData.Set("name", value); } + } + + public required string PrivateKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("privateKey"); + } + init { this._rawData.Set("privateKey", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// URL used in the Canonical header (if enabled). + /// + public string? BaseUrlForCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseUrlForCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseUrlForCanonicalHeader", value); + } + } + + /// + /// Whether to send a Canonical header. + /// + public bool? IncludeCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("includeCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("includeCanonicalHeader", value); + } + } + + public string? Prefix + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("prefix"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("prefix", value); + } + } + + /// + public override void Validate() + { + _ = this.Bucket; + _ = this.ClientEmail; + _ = this.Name; + _ = this.PrivateKey; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("GCS"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.BaseUrlForCanonicalHeader; + _ = this.IncludeCanonicalHeader; + _ = this.Prefix; + } + + public Gcs() + { + this.Type = JsonSerializer.SerializeToElement("GCS"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Gcs(Gcs gcs) + : base(gcs) { } +#pragma warning restore CS8618 + + public Gcs(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("GCS"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Gcs(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Gcs FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class GcsFromRaw : IFromRawJson +{ + /// + public Gcs FromRawUnchecked(IReadOnlyDictionary rawData) => + Gcs.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class AzureBlob : JsonModel +{ + public required string AccountName + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("accountName"); + } + init { this._rawData.Set("accountName", value); } + } + + public required string Container + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("container"); + } + init { this._rawData.Set("container", value); } + } + + /// + /// Display name of the origin. + /// + public required string Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("name"); + } + init { this._rawData.Set("name", value); } + } + + public required string SasToken + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("sasToken"); + } + init { this._rawData.Set("sasToken", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// URL used in the Canonical header (if enabled). + /// + public string? BaseUrlForCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseUrlForCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseUrlForCanonicalHeader", value); + } + } + + /// + /// Whether to send a Canonical header. + /// + public bool? IncludeCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("includeCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("includeCanonicalHeader", value); + } + } + + public string? Prefix + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("prefix"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("prefix", value); + } + } + + /// + public override void Validate() + { + _ = this.AccountName; + _ = this.Container; + _ = this.Name; + _ = this.SasToken; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("AZURE_BLOB"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.BaseUrlForCanonicalHeader; + _ = this.IncludeCanonicalHeader; + _ = this.Prefix; + } + + public AzureBlob() + { + this.Type = JsonSerializer.SerializeToElement("AZURE_BLOB"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public AzureBlob(AzureBlob azureBlob) + : base(azureBlob) { } +#pragma warning restore CS8618 + + public AzureBlob(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("AZURE_BLOB"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + AzureBlob(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static AzureBlob FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class AzureBlobFromRaw : IFromRawJson +{ + /// + public AzureBlob FromRawUnchecked(IReadOnlyDictionary rawData) => + AzureBlob.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class AkeneoPim : JsonModel +{ + /// + /// Akeneo instance base URL. + /// + public required string BaseUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("baseUrl"); + } + init { this._rawData.Set("baseUrl", value); } + } + + /// + /// Akeneo API client ID. + /// + public required string ClientID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("clientId"); + } + init { this._rawData.Set("clientId", value); } + } + + /// + /// Akeneo API client secret. + /// + public required string ClientSecret + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("clientSecret"); + } + init { this._rawData.Set("clientSecret", value); } + } + + /// + /// Display name of the origin. + /// + public required string Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("name"); + } + init { this._rawData.Set("name", value); } + } + + /// + /// Akeneo API password. + /// + public required string Password + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("password"); + } + init { this._rawData.Set("password", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Akeneo API username. + /// + public required string Username + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("username"); + } + init { this._rawData.Set("username", value); } + } + + /// + /// URL used in the Canonical header (if enabled). + /// + public string? BaseUrlForCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseUrlForCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseUrlForCanonicalHeader", value); + } + } + + /// + /// Whether to send a Canonical header. + /// + public bool? IncludeCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("includeCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("includeCanonicalHeader", value); + } + } + + /// + public override void Validate() + { + _ = this.BaseUrl; + _ = this.ClientID; + _ = this.ClientSecret; + _ = this.Name; + _ = this.Password; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("AKENEO_PIM"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.Username; + _ = this.BaseUrlForCanonicalHeader; + _ = this.IncludeCanonicalHeader; + } + + public AkeneoPim() + { + this.Type = JsonSerializer.SerializeToElement("AKENEO_PIM"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public AkeneoPim(AkeneoPim akeneoPim) + : base(akeneoPim) { } +#pragma warning restore CS8618 + + public AkeneoPim(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("AKENEO_PIM"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + AkeneoPim(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static AkeneoPim FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class AkeneoPimFromRaw : IFromRawJson +{ + /// + public AkeneoPim FromRawUnchecked(IReadOnlyDictionary rawData) => + AkeneoPim.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Accounts/Origins/OriginResponse.cs b/src/Imagekit/Models/Accounts/Origins/OriginResponse.cs new file mode 100644 index 00000000..b6044663 --- /dev/null +++ b/src/Imagekit/Models/Accounts/Origins/OriginResponse.cs @@ -0,0 +1,2064 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models.Accounts.Origins; + +/// +/// Origin object as returned by the API (sensitive fields removed). +/// +[JsonConverter(typeof(OriginResponseConverter))] +public record class OriginResponse : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public string ID + { + get + { + return Match( + s3: (x) => x.ID, + s3Compatible: (x) => x.ID, + cloudinaryBackup: (x) => x.ID, + webFolder: (x) => x.ID, + webProxy: (x) => x.ID, + gcs: (x) => x.ID, + azureBlob: (x) => x.ID, + akeneoPim: (x) => x.ID + ); + } + } + + public string? Bucket + { + get + { + return Match( + s3: (x) => x.Bucket, + s3Compatible: (x) => x.Bucket, + cloudinaryBackup: (x) => x.Bucket, + webFolder: (_) => null, + webProxy: (_) => null, + gcs: (x) => x.Bucket, + azureBlob: (_) => null, + akeneoPim: (_) => null + ); + } + } + + public bool IncludeCanonicalHeader + { + get + { + return Match( + s3: (x) => x.IncludeCanonicalHeader, + s3Compatible: (x) => x.IncludeCanonicalHeader, + cloudinaryBackup: (x) => x.IncludeCanonicalHeader, + webFolder: (x) => x.IncludeCanonicalHeader, + webProxy: (x) => x.IncludeCanonicalHeader, + gcs: (x) => x.IncludeCanonicalHeader, + azureBlob: (x) => x.IncludeCanonicalHeader, + akeneoPim: (x) => x.IncludeCanonicalHeader + ); + } + } + + public string Name + { + get + { + return Match( + s3: (x) => x.Name, + s3Compatible: (x) => x.Name, + cloudinaryBackup: (x) => x.Name, + webFolder: (x) => x.Name, + webProxy: (x) => x.Name, + gcs: (x) => x.Name, + azureBlob: (x) => x.Name, + akeneoPim: (x) => x.Name + ); + } + } + + public string? Prefix + { + get + { + return Match( + s3: (x) => x.Prefix, + s3Compatible: (x) => x.Prefix, + cloudinaryBackup: (x) => x.Prefix, + webFolder: (_) => null, + webProxy: (_) => null, + gcs: (x) => x.Prefix, + azureBlob: (x) => x.Prefix, + akeneoPim: (_) => null + ); + } + } + + public JsonElement Type + { + get + { + return Match( + s3: (x) => x.Type, + s3Compatible: (x) => x.Type, + cloudinaryBackup: (x) => x.Type, + webFolder: (x) => x.Type, + webProxy: (x) => x.Type, + gcs: (x) => x.Type, + azureBlob: (x) => x.Type, + akeneoPim: (x) => x.Type + ); + } + } + + public string? BaseUrlForCanonicalHeader + { + get + { + return Match( + s3: (x) => x.BaseUrlForCanonicalHeader, + s3Compatible: (x) => x.BaseUrlForCanonicalHeader, + cloudinaryBackup: (x) => x.BaseUrlForCanonicalHeader, + webFolder: (x) => x.BaseUrlForCanonicalHeader, + webProxy: (x) => x.BaseUrlForCanonicalHeader, + gcs: (x) => x.BaseUrlForCanonicalHeader, + azureBlob: (x) => x.BaseUrlForCanonicalHeader, + akeneoPim: (x) => x.BaseUrlForCanonicalHeader + ); + } + } + + public string? BaseUrl + { + get + { + return Match( + s3: (_) => null, + s3Compatible: (_) => null, + cloudinaryBackup: (_) => null, + webFolder: (x) => x.BaseUrl, + webProxy: (_) => null, + gcs: (_) => null, + azureBlob: (_) => null, + akeneoPim: (x) => x.BaseUrl + ); + } + } + + public OriginResponse(OriginResponseS3 value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OriginResponse(OriginResponseS3Compatible value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OriginResponse(OriginResponseCloudinaryBackup value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OriginResponse(OriginResponseWebFolder value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OriginResponse(OriginResponseWebProxy value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OriginResponse(OriginResponseGcs value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OriginResponse(OriginResponseAzureBlob value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OriginResponse(OriginResponseAkeneoPim value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OriginResponse(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickS3(out var value)) { + /// // `value` is of type `OriginResponseS3` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickS3([NotNullWhen(true)] out OriginResponseS3? value) + { + value = this.Value as OriginResponseS3; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickS3Compatible(out var value)) { + /// // `value` is of type `OriginResponseS3Compatible` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickS3Compatible([NotNullWhen(true)] out OriginResponseS3Compatible? value) + { + value = this.Value as OriginResponseS3Compatible; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickCloudinaryBackup(out var value)) { + /// // `value` is of type `OriginResponseCloudinaryBackup` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickCloudinaryBackup( + [NotNullWhen(true)] out OriginResponseCloudinaryBackup? value + ) + { + value = this.Value as OriginResponseCloudinaryBackup; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickWebFolder(out var value)) { + /// // `value` is of type `OriginResponseWebFolder` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickWebFolder([NotNullWhen(true)] out OriginResponseWebFolder? value) + { + value = this.Value as OriginResponseWebFolder; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickWebProxy(out var value)) { + /// // `value` is of type `OriginResponseWebProxy` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickWebProxy([NotNullWhen(true)] out OriginResponseWebProxy? value) + { + value = this.Value as OriginResponseWebProxy; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickGcs(out var value)) { + /// // `value` is of type `OriginResponseGcs` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickGcs([NotNullWhen(true)] out OriginResponseGcs? value) + { + value = this.Value as OriginResponseGcs; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickAzureBlob(out var value)) { + /// // `value` is of type `OriginResponseAzureBlob` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickAzureBlob([NotNullWhen(true)] out OriginResponseAzureBlob? value) + { + value = this.Value as OriginResponseAzureBlob; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickAkeneoPim(out var value)) { + /// // `value` is of type `OriginResponseAkeneoPim` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickAkeneoPim([NotNullWhen(true)] out OriginResponseAkeneoPim? value) + { + value = this.Value as OriginResponseAkeneoPim; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (OriginResponseS3 value) => {...}, + /// (OriginResponseS3Compatible value) => {...}, + /// (OriginResponseCloudinaryBackup value) => {...}, + /// (OriginResponseWebFolder value) => {...}, + /// (OriginResponseWebProxy value) => {...}, + /// (OriginResponseGcs value) => {...}, + /// (OriginResponseAzureBlob value) => {...}, + /// (OriginResponseAkeneoPim value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action s3, + System::Action s3Compatible, + System::Action cloudinaryBackup, + System::Action webFolder, + System::Action webProxy, + System::Action gcs, + System::Action azureBlob, + System::Action akeneoPim + ) + { + switch (this.Value) + { + case OriginResponseS3 value: + s3(value); + break; + case OriginResponseS3Compatible value: + s3Compatible(value); + break; + case OriginResponseCloudinaryBackup value: + cloudinaryBackup(value); + break; + case OriginResponseWebFolder value: + webFolder(value); + break; + case OriginResponseWebProxy value: + webProxy(value); + break; + case OriginResponseGcs value: + gcs(value); + break; + case OriginResponseAzureBlob value: + azureBlob(value); + break; + case OriginResponseAkeneoPim value: + akeneoPim(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of OriginResponse" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (OriginResponseS3 value) => {...}, + /// (OriginResponseS3Compatible value) => {...}, + /// (OriginResponseCloudinaryBackup value) => {...}, + /// (OriginResponseWebFolder value) => {...}, + /// (OriginResponseWebProxy value) => {...}, + /// (OriginResponseGcs value) => {...}, + /// (OriginResponseAzureBlob value) => {...}, + /// (OriginResponseAkeneoPim value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func s3, + System::Func s3Compatible, + System::Func cloudinaryBackup, + System::Func webFolder, + System::Func webProxy, + System::Func gcs, + System::Func azureBlob, + System::Func akeneoPim + ) + { + return this.Value switch + { + OriginResponseS3 value => s3(value), + OriginResponseS3Compatible value => s3Compatible(value), + OriginResponseCloudinaryBackup value => cloudinaryBackup(value), + OriginResponseWebFolder value => webFolder(value), + OriginResponseWebProxy value => webProxy(value), + OriginResponseGcs value => gcs(value), + OriginResponseAzureBlob value => azureBlob(value), + OriginResponseAkeneoPim value => akeneoPim(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of OriginResponse" + ), + }; + } + + public static implicit operator OriginResponse(OriginResponseS3 value) => new(value); + + public static implicit operator OriginResponse(OriginResponseS3Compatible value) => new(value); + + public static implicit operator OriginResponse(OriginResponseCloudinaryBackup value) => + new(value); + + public static implicit operator OriginResponse(OriginResponseWebFolder value) => new(value); + + public static implicit operator OriginResponse(OriginResponseWebProxy value) => new(value); + + public static implicit operator OriginResponse(OriginResponseGcs value) => new(value); + + public static implicit operator OriginResponse(OriginResponseAzureBlob value) => new(value); + + public static implicit operator OriginResponse(OriginResponseAkeneoPim value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of OriginResponse" + ); + } + this.Switch( + (s3) => s3.Validate(), + (s3Compatible) => s3Compatible.Validate(), + (cloudinaryBackup) => cloudinaryBackup.Validate(), + (webFolder) => webFolder.Validate(), + (webProxy) => webProxy.Validate(), + (gcs) => gcs.Validate(), + (azureBlob) => azureBlob.Validate(), + (akeneoPim) => akeneoPim.Validate() + ); + } + + public virtual bool Equals(OriginResponse? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + OriginResponseS3 _ => 0, + OriginResponseS3Compatible _ => 1, + OriginResponseCloudinaryBackup _ => 2, + OriginResponseWebFolder _ => 3, + OriginResponseWebProxy _ => 4, + OriginResponseGcs _ => 5, + OriginResponseAzureBlob _ => 6, + OriginResponseAkeneoPim _ => 7, + _ => -1, + }; + } +} + +sealed class OriginResponseConverter : JsonConverter +{ + public override OriginResponse? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + string? type; + try + { + type = element.GetProperty("type").GetString(); + } + catch + { + type = null; + } + + switch (type) + { + case "S3": + { + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "S3_COMPATIBLE": + { + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "CLOUDINARY_BACKUP": + { + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "WEB_FOLDER": + { + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "WEB_PROXY": + { + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "GCS": + { + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "AZURE_BLOB": + { + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "AKENEO_PIM": + { + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + default: + { + return new OriginResponse(element); + } + } + } + + public override void Write( + Utf8JsonWriter writer, + OriginResponse value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class OriginResponseS3 : JsonModel +{ + /// + /// Unique identifier for the origin. This is generated by ImageKit when you + /// create a new origin. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + /// + /// S3 bucket name. + /// + public required string Bucket + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("bucket"); + } + init { this._rawData.Set("bucket", value); } + } + + /// + /// Whether to send a Canonical header. + /// + public required bool IncludeCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("includeCanonicalHeader"); + } + init { this._rawData.Set("includeCanonicalHeader", value); } + } + + /// + /// Display name of the origin. + /// + public required string Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("name"); + } + init { this._rawData.Set("name", value); } + } + + /// + /// Path prefix inside the bucket. + /// + public required string Prefix + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("prefix"); + } + init { this._rawData.Set("prefix", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// URL used in the Canonical header (if enabled). + /// + public string? BaseUrlForCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseUrlForCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseUrlForCanonicalHeader", value); + } + } + + /// + public override void Validate() + { + _ = this.ID; + _ = this.Bucket; + _ = this.IncludeCanonicalHeader; + _ = this.Name; + _ = this.Prefix; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("S3"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.BaseUrlForCanonicalHeader; + } + + public OriginResponseS3() + { + this.Type = JsonSerializer.SerializeToElement("S3"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public OriginResponseS3(OriginResponseS3 originResponseS3) + : base(originResponseS3) { } +#pragma warning restore CS8618 + + public OriginResponseS3(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("S3"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + OriginResponseS3(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static OriginResponseS3 FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class OriginResponseS3FromRaw : IFromRawJson +{ + /// + public OriginResponseS3 FromRawUnchecked(IReadOnlyDictionary rawData) => + OriginResponseS3.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter) +)] +public sealed record class OriginResponseS3Compatible : JsonModel +{ + /// + /// Unique identifier for the origin. This is generated by ImageKit when you + /// create a new origin. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + /// + /// S3 bucket name. + /// + public required string Bucket + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("bucket"); + } + init { this._rawData.Set("bucket", value); } + } + + /// + /// Custom S3-compatible endpoint. + /// + public required string Endpoint + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("endpoint"); + } + init { this._rawData.Set("endpoint", value); } + } + + /// + /// Whether to send a Canonical header. + /// + public required bool IncludeCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("includeCanonicalHeader"); + } + init { this._rawData.Set("includeCanonicalHeader", value); } + } + + /// + /// Display name of the origin. + /// + public required string Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("name"); + } + init { this._rawData.Set("name", value); } + } + + /// + /// Path prefix inside the bucket. + /// + public required string Prefix + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("prefix"); + } + init { this._rawData.Set("prefix", value); } + } + + /// + /// Use path-style S3 URLs? + /// + public required bool S3ForcePathStyle + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("s3ForcePathStyle"); + } + init { this._rawData.Set("s3ForcePathStyle", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// URL used in the Canonical header (if enabled). + /// + public string? BaseUrlForCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseUrlForCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseUrlForCanonicalHeader", value); + } + } + + /// + public override void Validate() + { + _ = this.ID; + _ = this.Bucket; + _ = this.Endpoint; + _ = this.IncludeCanonicalHeader; + _ = this.Name; + _ = this.Prefix; + _ = this.S3ForcePathStyle; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("S3_COMPATIBLE"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.BaseUrlForCanonicalHeader; + } + + public OriginResponseS3Compatible() + { + this.Type = JsonSerializer.SerializeToElement("S3_COMPATIBLE"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public OriginResponseS3Compatible(OriginResponseS3Compatible originResponseS3Compatible) + : base(originResponseS3Compatible) { } +#pragma warning restore CS8618 + + public OriginResponseS3Compatible(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("S3_COMPATIBLE"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + OriginResponseS3Compatible(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static OriginResponseS3Compatible FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class OriginResponseS3CompatibleFromRaw : IFromRawJson +{ + /// + public OriginResponseS3Compatible FromRawUnchecked( + IReadOnlyDictionary rawData + ) => OriginResponseS3Compatible.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter< + OriginResponseCloudinaryBackup, + OriginResponseCloudinaryBackupFromRaw + >) +)] +public sealed record class OriginResponseCloudinaryBackup : JsonModel +{ + /// + /// Unique identifier for the origin. This is generated by ImageKit when you + /// create a new origin. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + /// + /// S3 bucket name. + /// + public required string Bucket + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("bucket"); + } + init { this._rawData.Set("bucket", value); } + } + + /// + /// Whether to send a Canonical header. + /// + public required bool IncludeCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("includeCanonicalHeader"); + } + init { this._rawData.Set("includeCanonicalHeader", value); } + } + + /// + /// Display name of the origin. + /// + public required string Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("name"); + } + init { this._rawData.Set("name", value); } + } + + /// + /// Path prefix inside the bucket. + /// + public required string Prefix + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("prefix"); + } + init { this._rawData.Set("prefix", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// URL used in the Canonical header (if enabled). + /// + public string? BaseUrlForCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseUrlForCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseUrlForCanonicalHeader", value); + } + } + + /// + public override void Validate() + { + _ = this.ID; + _ = this.Bucket; + _ = this.IncludeCanonicalHeader; + _ = this.Name; + _ = this.Prefix; + if ( + !JsonElement.DeepEquals( + this.Type, + JsonSerializer.SerializeToElement("CLOUDINARY_BACKUP") + ) + ) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.BaseUrlForCanonicalHeader; + } + + public OriginResponseCloudinaryBackup() + { + this.Type = JsonSerializer.SerializeToElement("CLOUDINARY_BACKUP"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public OriginResponseCloudinaryBackup( + OriginResponseCloudinaryBackup originResponseCloudinaryBackup + ) + : base(originResponseCloudinaryBackup) { } +#pragma warning restore CS8618 + + public OriginResponseCloudinaryBackup(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("CLOUDINARY_BACKUP"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + OriginResponseCloudinaryBackup(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static OriginResponseCloudinaryBackup FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class OriginResponseCloudinaryBackupFromRaw : IFromRawJson +{ + /// + public OriginResponseCloudinaryBackup FromRawUnchecked( + IReadOnlyDictionary rawData + ) => OriginResponseCloudinaryBackup.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class OriginResponseWebFolder : JsonModel +{ + /// + /// Unique identifier for the origin. This is generated by ImageKit when you + /// create a new origin. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + /// + /// Root URL for the web folder origin. + /// + public required string BaseUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("baseUrl"); + } + init { this._rawData.Set("baseUrl", value); } + } + + /// + /// Forward the Host header to origin? + /// + public required bool ForwardHostHeaderToOrigin + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("forwardHostHeaderToOrigin"); + } + init { this._rawData.Set("forwardHostHeaderToOrigin", value); } + } + + /// + /// Whether to send a Canonical header. + /// + public required bool IncludeCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("includeCanonicalHeader"); + } + init { this._rawData.Set("includeCanonicalHeader", value); } + } + + /// + /// Display name of the origin. + /// + public required string Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("name"); + } + init { this._rawData.Set("name", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// URL used in the Canonical header (if enabled). + /// + public string? BaseUrlForCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseUrlForCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseUrlForCanonicalHeader", value); + } + } + + /// + public override void Validate() + { + _ = this.ID; + _ = this.BaseUrl; + _ = this.ForwardHostHeaderToOrigin; + _ = this.IncludeCanonicalHeader; + _ = this.Name; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("WEB_FOLDER"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.BaseUrlForCanonicalHeader; + } + + public OriginResponseWebFolder() + { + this.Type = JsonSerializer.SerializeToElement("WEB_FOLDER"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public OriginResponseWebFolder(OriginResponseWebFolder originResponseWebFolder) + : base(originResponseWebFolder) { } +#pragma warning restore CS8618 + + public OriginResponseWebFolder(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("WEB_FOLDER"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + OriginResponseWebFolder(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static OriginResponseWebFolder FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class OriginResponseWebFolderFromRaw : IFromRawJson +{ + /// + public OriginResponseWebFolder FromRawUnchecked( + IReadOnlyDictionary rawData + ) => OriginResponseWebFolder.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class OriginResponseWebProxy : JsonModel +{ + /// + /// Unique identifier for the origin. This is generated by ImageKit when you + /// create a new origin. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + /// + /// Whether to send a Canonical header. + /// + public required bool IncludeCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("includeCanonicalHeader"); + } + init { this._rawData.Set("includeCanonicalHeader", value); } + } + + /// + /// Display name of the origin. + /// + public required string Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("name"); + } + init { this._rawData.Set("name", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// URL used in the Canonical header (if enabled). + /// + public string? BaseUrlForCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseUrlForCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseUrlForCanonicalHeader", value); + } + } + + /// + public override void Validate() + { + _ = this.ID; + _ = this.IncludeCanonicalHeader; + _ = this.Name; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("WEB_PROXY"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.BaseUrlForCanonicalHeader; + } + + public OriginResponseWebProxy() + { + this.Type = JsonSerializer.SerializeToElement("WEB_PROXY"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public OriginResponseWebProxy(OriginResponseWebProxy originResponseWebProxy) + : base(originResponseWebProxy) { } +#pragma warning restore CS8618 + + public OriginResponseWebProxy(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("WEB_PROXY"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + OriginResponseWebProxy(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static OriginResponseWebProxy FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class OriginResponseWebProxyFromRaw : IFromRawJson +{ + /// + public OriginResponseWebProxy FromRawUnchecked( + IReadOnlyDictionary rawData + ) => OriginResponseWebProxy.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class OriginResponseGcs : JsonModel +{ + /// + /// Unique identifier for the origin. This is generated by ImageKit when you + /// create a new origin. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + public required string Bucket + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("bucket"); + } + init { this._rawData.Set("bucket", value); } + } + + public required string ClientEmail + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("clientEmail"); + } + init { this._rawData.Set("clientEmail", value); } + } + + /// + /// Whether to send a Canonical header. + /// + public required bool IncludeCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("includeCanonicalHeader"); + } + init { this._rawData.Set("includeCanonicalHeader", value); } + } + + /// + /// Display name of the origin. + /// + public required string Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("name"); + } + init { this._rawData.Set("name", value); } + } + + public required string Prefix + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("prefix"); + } + init { this._rawData.Set("prefix", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// URL used in the Canonical header (if enabled). + /// + public string? BaseUrlForCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseUrlForCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseUrlForCanonicalHeader", value); + } + } + + /// + public override void Validate() + { + _ = this.ID; + _ = this.Bucket; + _ = this.ClientEmail; + _ = this.IncludeCanonicalHeader; + _ = this.Name; + _ = this.Prefix; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("GCS"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.BaseUrlForCanonicalHeader; + } + + public OriginResponseGcs() + { + this.Type = JsonSerializer.SerializeToElement("GCS"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public OriginResponseGcs(OriginResponseGcs originResponseGcs) + : base(originResponseGcs) { } +#pragma warning restore CS8618 + + public OriginResponseGcs(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("GCS"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + OriginResponseGcs(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static OriginResponseGcs FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class OriginResponseGcsFromRaw : IFromRawJson +{ + /// + public OriginResponseGcs FromRawUnchecked(IReadOnlyDictionary rawData) => + OriginResponseGcs.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class OriginResponseAzureBlob : JsonModel +{ + /// + /// Unique identifier for the origin. This is generated by ImageKit when you + /// create a new origin. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + public required string AccountName + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("accountName"); + } + init { this._rawData.Set("accountName", value); } + } + + public required string Container + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("container"); + } + init { this._rawData.Set("container", value); } + } + + /// + /// Whether to send a Canonical header. + /// + public required bool IncludeCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("includeCanonicalHeader"); + } + init { this._rawData.Set("includeCanonicalHeader", value); } + } + + /// + /// Display name of the origin. + /// + public required string Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("name"); + } + init { this._rawData.Set("name", value); } + } + + public required string Prefix + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("prefix"); + } + init { this._rawData.Set("prefix", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// URL used in the Canonical header (if enabled). + /// + public string? BaseUrlForCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseUrlForCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseUrlForCanonicalHeader", value); + } + } + + /// + public override void Validate() + { + _ = this.ID; + _ = this.AccountName; + _ = this.Container; + _ = this.IncludeCanonicalHeader; + _ = this.Name; + _ = this.Prefix; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("AZURE_BLOB"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.BaseUrlForCanonicalHeader; + } + + public OriginResponseAzureBlob() + { + this.Type = JsonSerializer.SerializeToElement("AZURE_BLOB"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public OriginResponseAzureBlob(OriginResponseAzureBlob originResponseAzureBlob) + : base(originResponseAzureBlob) { } +#pragma warning restore CS8618 + + public OriginResponseAzureBlob(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("AZURE_BLOB"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + OriginResponseAzureBlob(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static OriginResponseAzureBlob FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class OriginResponseAzureBlobFromRaw : IFromRawJson +{ + /// + public OriginResponseAzureBlob FromRawUnchecked( + IReadOnlyDictionary rawData + ) => OriginResponseAzureBlob.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class OriginResponseAkeneoPim : JsonModel +{ + /// + /// Unique identifier for the origin. This is generated by ImageKit when you + /// create a new origin. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + /// + /// Akeneo instance base URL. + /// + public required string BaseUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("baseUrl"); + } + init { this._rawData.Set("baseUrl", value); } + } + + /// + /// Whether to send a Canonical header. + /// + public required bool IncludeCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("includeCanonicalHeader"); + } + init { this._rawData.Set("includeCanonicalHeader", value); } + } + + /// + /// Display name of the origin. + /// + public required string Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("name"); + } + init { this._rawData.Set("name", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// URL used in the Canonical header (if enabled). + /// + public string? BaseUrlForCanonicalHeader + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseUrlForCanonicalHeader"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseUrlForCanonicalHeader", value); + } + } + + /// + public override void Validate() + { + _ = this.ID; + _ = this.BaseUrl; + _ = this.IncludeCanonicalHeader; + _ = this.Name; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("AKENEO_PIM"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.BaseUrlForCanonicalHeader; + } + + public OriginResponseAkeneoPim() + { + this.Type = JsonSerializer.SerializeToElement("AKENEO_PIM"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public OriginResponseAkeneoPim(OriginResponseAkeneoPim originResponseAkeneoPim) + : base(originResponseAkeneoPim) { } +#pragma warning restore CS8618 + + public OriginResponseAkeneoPim(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("AKENEO_PIM"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + OriginResponseAkeneoPim(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static OriginResponseAkeneoPim FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class OriginResponseAkeneoPimFromRaw : IFromRawJson +{ + /// + public OriginResponseAkeneoPim FromRawUnchecked( + IReadOnlyDictionary rawData + ) => OriginResponseAkeneoPim.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Accounts/Origins/OriginUpdateParams.cs b/src/Imagekit/Models/Accounts/Origins/OriginUpdateParams.cs new file mode 100644 index 00000000..6b4b4283 --- /dev/null +++ b/src/Imagekit/Models/Accounts/Origins/OriginUpdateParams.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; +using Text = System.Text; + +namespace Imagekit.Models.Accounts.Origins; + +/// +/// **Note:** This API is currently in beta. Updates the origin identified by `id` +/// and returns the updated origin object. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class OriginUpdateParams : ParamsBase +{ + public JsonElement RawBodyData { get; private init; } + + public string? ID { get; init; } + + /// + /// Schema for origin request resources. + /// + public required OriginRequest OriginRequest + { + get + { + return WrappedJsonSerializer.GetNotNullClass( + this.RawBodyData, + "RawBodyData" + ); + } + init { this.RawBodyData = JsonSerializer.SerializeToElement(value); } + } + + public OriginUpdateParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public OriginUpdateParams(OriginUpdateParams originUpdateParams) + : base(originUpdateParams) + { + this.ID = originUpdateParams.ID; + + this.RawBodyData = originUpdateParams.RawBodyData; + } +#pragma warning restore CS8618 + + public OriginUpdateParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + JsonElement rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this.RawBodyData = rawBodyData; + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + OriginUpdateParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + JsonElement rawBodyData, + string id + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this.RawBodyData = rawBodyData; + this.ID = id; + } +#pragma warning restore CS8618 + + /// + public static OriginUpdateParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + JsonElement rawBodyData, + string id + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + rawBodyData, + id + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["ID"] = JsonSerializer.SerializeToElement(this.ID), + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this.RawBodyData), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(OriginUpdateParams? other) + { + if (other == null) + { + return false; + } + return (this.ID?.Equals(other.ID) ?? other.ID == null) + && this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this.RawBodyData.Equals(other.RawBodyData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + + string.Format("/v1/accounts/origins/{0}", this.ID) + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Accounts/UrlEndpoints/UrlEndpointCreateParams.cs b/src/Imagekit/Models/Accounts/UrlEndpoints/UrlEndpointCreateParams.cs new file mode 100644 index 00000000..ca3df570 --- /dev/null +++ b/src/Imagekit/Models/Accounts/UrlEndpoints/UrlEndpointCreateParams.cs @@ -0,0 +1,786 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; +using Text = System.Text; + +namespace Imagekit.Models.Accounts.UrlEndpoints; + +/// +/// **Note:** This API is currently in beta. Creates a new URL‑endpoint and returns +/// the resulting object. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class UrlEndpointCreateParams : ParamsBase +{ + readonly JsonDictionary _rawBodyData = new(); + public IReadOnlyDictionary RawBodyData + { + get { return this._rawBodyData.Freeze(); } + } + + /// + /// Description of the URL endpoint. + /// + public required string Description + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("description"); + } + init { this._rawBodyData.Set("description", value); } + } + + /// + /// Ordered list of origin IDs to try when the file isn’t in the Media Library; + /// ImageKit checks them in the sequence provided. Origin must be created before + /// it can be used in a URL endpoint. + /// + public IReadOnlyList? Origins + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct>("origins"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set?>( + "origins", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Path segment appended to your base URL to form the endpoint (letters, digits, + /// and hyphens only — or empty for the default endpoint). + /// + public string? UrlPrefix + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("urlPrefix"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("urlPrefix", value); + } + } + + /// + /// Configuration for third-party URL rewriting. + /// + public UrlRewriter? UrlRewriter + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("urlRewriter"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("urlRewriter", value); + } + } + + public UrlEndpointCreateParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UrlEndpointCreateParams(UrlEndpointCreateParams urlEndpointCreateParams) + : base(urlEndpointCreateParams) + { + this._rawBodyData = new(urlEndpointCreateParams._rawBodyData); + } +#pragma warning restore CS8618 + + public UrlEndpointCreateParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UrlEndpointCreateParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + FrozenDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } +#pragma warning restore CS8618 + + /// + public static UrlEndpointCreateParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + FrozenDictionary.ToFrozenDictionary(rawBodyData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this._rawBodyData.Freeze()), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(UrlEndpointCreateParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this._rawBodyData.Equals(other._rawBodyData); + } + + public override System::Uri Url(ClientOptions options) + { + return new System::UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + "/v1/accounts/url-endpoints" + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} + +/// +/// Configuration for third-party URL rewriting. +/// +[JsonConverter(typeof(UrlRewriterConverter))] +public record class UrlRewriter : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public UrlRewriter(Cloudinary value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UrlRewriter(Imgix value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UrlRewriter(Akamai value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UrlRewriter(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickCloudinary(out var value)) { + /// // `value` is of type `Cloudinary` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickCloudinary([NotNullWhen(true)] out Cloudinary? value) + { + value = this.Value as Cloudinary; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickImgix(out var value)) { + /// // `value` is of type `Imgix` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickImgix([NotNullWhen(true)] out Imgix? value) + { + value = this.Value as Imgix; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickAkamai(out var value)) { + /// // `value` is of type `Akamai` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickAkamai([NotNullWhen(true)] out Akamai? value) + { + value = this.Value as Akamai; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (Cloudinary value) => {...}, + /// (Imgix value) => {...}, + /// (Akamai value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action cloudinary, + System::Action imgix, + System::Action akamai + ) + { + switch (this.Value) + { + case Cloudinary value: + cloudinary(value); + break; + case Imgix value: + imgix(value); + break; + case Akamai value: + akamai(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of UrlRewriter" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (Cloudinary value) => {...}, + /// (Imgix value) => {...}, + /// (Akamai value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func cloudinary, + System::Func imgix, + System::Func akamai + ) + { + return this.Value switch + { + Cloudinary value => cloudinary(value), + Imgix value => imgix(value), + Akamai value => akamai(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of UrlRewriter" + ), + }; + } + + public static implicit operator UrlRewriter(Cloudinary value) => new(value); + + public static implicit operator UrlRewriter(Imgix value) => new(value); + + public static implicit operator UrlRewriter(Akamai value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of UrlRewriter"); + } + this.Switch( + (cloudinary) => cloudinary.Validate(), + (imgix) => imgix.Validate(), + (akamai) => akamai.Validate() + ); + } + + public virtual bool Equals(UrlRewriter? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + Cloudinary _ => 0, + Imgix _ => 1, + Akamai _ => 2, + _ => -1, + }; + } +} + +sealed class UrlRewriterConverter : JsonConverter +{ + public override UrlRewriter? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + string? type; + try + { + type = element.GetProperty("type").GetString(); + } + catch + { + type = null; + } + + switch (type) + { + case "CLOUDINARY": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "IMGIX": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "AKAMAI": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + default: + { + return new UrlRewriter(element); + } + } + } + + public override void Write( + Utf8JsonWriter writer, + UrlRewriter value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Cloudinary : JsonModel +{ + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Whether to preserve `<asset_type>/<delivery_type>` in the rewritten URL. + /// + public bool? PreserveAssetDeliveryTypes + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("preserveAssetDeliveryTypes"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("preserveAssetDeliveryTypes", value); + } + } + + /// + public override void Validate() + { + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("CLOUDINARY"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.PreserveAssetDeliveryTypes; + } + + public Cloudinary() + { + this.Type = JsonSerializer.SerializeToElement("CLOUDINARY"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Cloudinary(Cloudinary cloudinary) + : base(cloudinary) { } +#pragma warning restore CS8618 + + public Cloudinary(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("CLOUDINARY"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Cloudinary(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Cloudinary FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class CloudinaryFromRaw : IFromRawJson +{ + /// + public Cloudinary FromRawUnchecked(IReadOnlyDictionary rawData) => + Cloudinary.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(ImgixConverter))] +public record class Imgix +{ + public JsonElement Element { get; private init; } + + public Imgix() + { + Element = JsonSerializer.Deserialize( + """ + { + "type": "IMGIX" + } + """ + ); + } + + internal Imgix(JsonElement element) + { + Element = element; + } + + /// + /// Validates that the instance's underlying value is the expected constant. + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public void Validate() + { + if (this != new Imgix()) + { + throw new ImageKitInvalidDataException("Invalid value given for 'Imgix'"); + } + } + + public override int GetHashCode() + { + return 0; + } + + public virtual bool Equals(Imgix? other) + { + if (other == null) + { + return false; + } + + return JsonElement.DeepEquals(this.Element, other.Element); + } +} + +class ImgixConverter : JsonConverter +{ + public override Imgix? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return new(JsonSerializer.Deserialize(ref reader, options)); + } + + public override void Write(Utf8JsonWriter writer, Imgix value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Element, options); + } +} + +[JsonConverter(typeof(AkamaiConverter))] +public record class Akamai +{ + public JsonElement Element { get; private init; } + + public Akamai() + { + Element = JsonSerializer.Deserialize( + """ + { + "type": "AKAMAI" + } + """ + ); + } + + internal Akamai(JsonElement element) + { + Element = element; + } + + /// + /// Validates that the instance's underlying value is the expected constant. + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public void Validate() + { + if (this != new Akamai()) + { + throw new ImageKitInvalidDataException("Invalid value given for 'Akamai'"); + } + } + + public override int GetHashCode() + { + return 0; + } + + public virtual bool Equals(Akamai? other) + { + if (other == null) + { + return false; + } + + return JsonElement.DeepEquals(this.Element, other.Element); + } +} + +class AkamaiConverter : JsonConverter +{ + public override Akamai? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return new(JsonSerializer.Deserialize(ref reader, options)); + } + + public override void Write(Utf8JsonWriter writer, Akamai value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Element, options); + } +} diff --git a/src/Imagekit/Models/Accounts/UrlEndpoints/UrlEndpointDeleteParams.cs b/src/Imagekit/Models/Accounts/UrlEndpoints/UrlEndpointDeleteParams.cs new file mode 100644 index 00000000..3fd9ba3a --- /dev/null +++ b/src/Imagekit/Models/Accounts/UrlEndpoints/UrlEndpointDeleteParams.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Models.Accounts.UrlEndpoints; + +/// +/// **Note:** This API is currently in beta. Deletes the URL‑endpoint identified +/// by `id`. You cannot delete the default URL‑endpoint created by ImageKit during +/// account creation. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class UrlEndpointDeleteParams : ParamsBase +{ + public string? ID { get; init; } + + public UrlEndpointDeleteParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UrlEndpointDeleteParams(UrlEndpointDeleteParams urlEndpointDeleteParams) + : base(urlEndpointDeleteParams) + { + this.ID = urlEndpointDeleteParams.ID; + } +#pragma warning restore CS8618 + + public UrlEndpointDeleteParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UrlEndpointDeleteParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + string id + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this.ID = id; + } +#pragma warning restore CS8618 + + /// + public static UrlEndpointDeleteParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + string id + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + id + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["ID"] = JsonSerializer.SerializeToElement(this.ID), + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(UrlEndpointDeleteParams? other) + { + if (other == null) + { + return false; + } + return (this.ID?.Equals(other.ID) ?? other.ID == null) + && this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + + string.Format("/v1/accounts/url-endpoints/{0}", this.ID) + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Accounts/UrlEndpoints/UrlEndpointGetParams.cs b/src/Imagekit/Models/Accounts/UrlEndpoints/UrlEndpointGetParams.cs new file mode 100644 index 00000000..5429a7c0 --- /dev/null +++ b/src/Imagekit/Models/Accounts/UrlEndpoints/UrlEndpointGetParams.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Models.Accounts.UrlEndpoints; + +/// +/// **Note:** This API is currently in beta. Retrieves the URL‑endpoint identified +/// by `id`. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class UrlEndpointGetParams : ParamsBase +{ + public string? ID { get; init; } + + public UrlEndpointGetParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UrlEndpointGetParams(UrlEndpointGetParams urlEndpointGetParams) + : base(urlEndpointGetParams) + { + this.ID = urlEndpointGetParams.ID; + } +#pragma warning restore CS8618 + + public UrlEndpointGetParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UrlEndpointGetParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + string id + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this.ID = id; + } +#pragma warning restore CS8618 + + /// + public static UrlEndpointGetParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + string id + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + id + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["ID"] = JsonSerializer.SerializeToElement(this.ID), + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(UrlEndpointGetParams? other) + { + if (other == null) + { + return false; + } + return (this.ID?.Equals(other.ID) ?? other.ID == null) + && this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + + string.Format("/v1/accounts/url-endpoints/{0}", this.ID) + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Accounts/UrlEndpoints/UrlEndpointListParams.cs b/src/Imagekit/Models/Accounts/UrlEndpoints/UrlEndpointListParams.cs new file mode 100644 index 00000000..03a85f65 --- /dev/null +++ b/src/Imagekit/Models/Accounts/UrlEndpoints/UrlEndpointListParams.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Models.Accounts.UrlEndpoints; + +/// +/// **Note:** This API is currently in beta. Returns an array of all URL‑endpoints +/// configured including the default URL-endpoint generated by ImageKit during account creation. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class UrlEndpointListParams : ParamsBase +{ + public UrlEndpointListParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UrlEndpointListParams(UrlEndpointListParams urlEndpointListParams) + : base(urlEndpointListParams) { } +#pragma warning restore CS8618 + + public UrlEndpointListParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UrlEndpointListParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } +#pragma warning restore CS8618 + + /// + public static UrlEndpointListParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(UrlEndpointListParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + "/v1/accounts/url-endpoints" + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Accounts/UrlEndpoints/UrlEndpointRequest.cs b/src/Imagekit/Models/Accounts/UrlEndpoints/UrlEndpointRequest.cs new file mode 100644 index 00000000..eb798448 --- /dev/null +++ b/src/Imagekit/Models/Accounts/UrlEndpoints/UrlEndpointRequest.cs @@ -0,0 +1,771 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models.Accounts.UrlEndpoints; + +/// +/// Schema for URL endpoint resource. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class UrlEndpointRequest : JsonModel +{ + /// + /// Description of the URL endpoint. + /// + public required string Description + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("description"); + } + init { this._rawData.Set("description", value); } + } + + /// + /// Ordered list of origin IDs to try when the file isn’t in the Media Library; + /// ImageKit checks them in the sequence provided. Origin must be created before + /// it can be used in a URL endpoint. + /// + public IReadOnlyList? Origins + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("origins"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "origins", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Path segment appended to your base URL to form the endpoint (letters, digits, + /// and hyphens only — or empty for the default endpoint). + /// + public string? UrlPrefix + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("urlPrefix"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("urlPrefix", value); + } + } + + /// + /// Configuration for third-party URL rewriting. + /// + public UrlEndpointRequestUrlRewriter? UrlRewriter + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("urlRewriter"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("urlRewriter", value); + } + } + + /// + public override void Validate() + { + _ = this.Description; + _ = this.Origins; + _ = this.UrlPrefix; + this.UrlRewriter?.Validate(); + } + + public UrlEndpointRequest() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UrlEndpointRequest(UrlEndpointRequest urlEndpointRequest) + : base(urlEndpointRequest) { } +#pragma warning restore CS8618 + + public UrlEndpointRequest(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UrlEndpointRequest(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UrlEndpointRequest FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public UrlEndpointRequest(string description) + : this() + { + this.Description = description; + } +} + +class UrlEndpointRequestFromRaw : IFromRawJson +{ + /// + public UrlEndpointRequest FromRawUnchecked(IReadOnlyDictionary rawData) => + UrlEndpointRequest.FromRawUnchecked(rawData); +} + +/// +/// Configuration for third-party URL rewriting. +/// +[JsonConverter(typeof(UrlEndpointRequestUrlRewriterConverter))] +public record class UrlEndpointRequestUrlRewriter : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public UrlEndpointRequestUrlRewriter( + UrlEndpointRequestUrlRewriterCloudinary value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public UrlEndpointRequestUrlRewriter( + UrlEndpointRequestUrlRewriterImgix value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public UrlEndpointRequestUrlRewriter( + UrlEndpointRequestUrlRewriterAkamai value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public UrlEndpointRequestUrlRewriter(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickCloudinary(out var value)) { + /// // `value` is of type `UrlEndpointRequestUrlRewriterCloudinary` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickCloudinary( + [NotNullWhen(true)] out UrlEndpointRequestUrlRewriterCloudinary? value + ) + { + value = this.Value as UrlEndpointRequestUrlRewriterCloudinary; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickImgix(out var value)) { + /// // `value` is of type `UrlEndpointRequestUrlRewriterImgix` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickImgix([NotNullWhen(true)] out UrlEndpointRequestUrlRewriterImgix? value) + { + value = this.Value as UrlEndpointRequestUrlRewriterImgix; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickAkamai(out var value)) { + /// // `value` is of type `UrlEndpointRequestUrlRewriterAkamai` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickAkamai([NotNullWhen(true)] out UrlEndpointRequestUrlRewriterAkamai? value) + { + value = this.Value as UrlEndpointRequestUrlRewriterAkamai; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (UrlEndpointRequestUrlRewriterCloudinary value) => {...}, + /// (UrlEndpointRequestUrlRewriterImgix value) => {...}, + /// (UrlEndpointRequestUrlRewriterAkamai value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action cloudinary, + System::Action imgix, + System::Action akamai + ) + { + switch (this.Value) + { + case UrlEndpointRequestUrlRewriterCloudinary value: + cloudinary(value); + break; + case UrlEndpointRequestUrlRewriterImgix value: + imgix(value); + break; + case UrlEndpointRequestUrlRewriterAkamai value: + akamai(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of UrlEndpointRequestUrlRewriter" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (UrlEndpointRequestUrlRewriterCloudinary value) => {...}, + /// (UrlEndpointRequestUrlRewriterImgix value) => {...}, + /// (UrlEndpointRequestUrlRewriterAkamai value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func cloudinary, + System::Func imgix, + System::Func akamai + ) + { + return this.Value switch + { + UrlEndpointRequestUrlRewriterCloudinary value => cloudinary(value), + UrlEndpointRequestUrlRewriterImgix value => imgix(value), + UrlEndpointRequestUrlRewriterAkamai value => akamai(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of UrlEndpointRequestUrlRewriter" + ), + }; + } + + public static implicit operator UrlEndpointRequestUrlRewriter( + UrlEndpointRequestUrlRewriterCloudinary value + ) => new(value); + + public static implicit operator UrlEndpointRequestUrlRewriter( + UrlEndpointRequestUrlRewriterImgix value + ) => new(value); + + public static implicit operator UrlEndpointRequestUrlRewriter( + UrlEndpointRequestUrlRewriterAkamai value + ) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of UrlEndpointRequestUrlRewriter" + ); + } + this.Switch( + (cloudinary) => cloudinary.Validate(), + (imgix) => imgix.Validate(), + (akamai) => akamai.Validate() + ); + } + + public virtual bool Equals(UrlEndpointRequestUrlRewriter? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + UrlEndpointRequestUrlRewriterCloudinary _ => 0, + UrlEndpointRequestUrlRewriterImgix _ => 1, + UrlEndpointRequestUrlRewriterAkamai _ => 2, + _ => -1, + }; + } +} + +sealed class UrlEndpointRequestUrlRewriterConverter : JsonConverter +{ + public override UrlEndpointRequestUrlRewriter? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + string? type; + try + { + type = element.GetProperty("type").GetString(); + } + catch + { + type = null; + } + + switch (type) + { + case "CLOUDINARY": + { + try + { + var deserialized = + JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "IMGIX": + { + try + { + var deserialized = + JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "AKAMAI": + { + try + { + var deserialized = + JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + default: + { + return new UrlEndpointRequestUrlRewriter(element); + } + } + } + + public override void Write( + Utf8JsonWriter writer, + UrlEndpointRequestUrlRewriter value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter( + typeof(JsonModelConverter< + UrlEndpointRequestUrlRewriterCloudinary, + UrlEndpointRequestUrlRewriterCloudinaryFromRaw + >) +)] +public sealed record class UrlEndpointRequestUrlRewriterCloudinary : JsonModel +{ + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Whether to preserve `<asset_type>/<delivery_type>` in the rewritten URL. + /// + public bool? PreserveAssetDeliveryTypes + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("preserveAssetDeliveryTypes"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("preserveAssetDeliveryTypes", value); + } + } + + /// + public override void Validate() + { + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("CLOUDINARY"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.PreserveAssetDeliveryTypes; + } + + public UrlEndpointRequestUrlRewriterCloudinary() + { + this.Type = JsonSerializer.SerializeToElement("CLOUDINARY"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UrlEndpointRequestUrlRewriterCloudinary( + UrlEndpointRequestUrlRewriterCloudinary urlEndpointRequestUrlRewriterCloudinary + ) + : base(urlEndpointRequestUrlRewriterCloudinary) { } +#pragma warning restore CS8618 + + public UrlEndpointRequestUrlRewriterCloudinary(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("CLOUDINARY"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UrlEndpointRequestUrlRewriterCloudinary(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UrlEndpointRequestUrlRewriterCloudinary FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class UrlEndpointRequestUrlRewriterCloudinaryFromRaw + : IFromRawJson +{ + /// + public UrlEndpointRequestUrlRewriterCloudinary FromRawUnchecked( + IReadOnlyDictionary rawData + ) => UrlEndpointRequestUrlRewriterCloudinary.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(UrlEndpointRequestUrlRewriterImgixConverter))] +public record class UrlEndpointRequestUrlRewriterImgix +{ + public JsonElement Element { get; private init; } + + public UrlEndpointRequestUrlRewriterImgix() + { + Element = JsonSerializer.Deserialize( + """ + { + "type": "IMGIX" + } + """ + ); + } + + internal UrlEndpointRequestUrlRewriterImgix(JsonElement element) + { + Element = element; + } + + /// + /// Validates that the instance's underlying value is the expected constant. + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public void Validate() + { + if (this != new UrlEndpointRequestUrlRewriterImgix()) + { + throw new ImageKitInvalidDataException( + "Invalid value given for 'UrlEndpointRequestUrlRewriterImgix'" + ); + } + } + + public override int GetHashCode() + { + return 0; + } + + public virtual bool Equals(UrlEndpointRequestUrlRewriterImgix? other) + { + if (other == null) + { + return false; + } + + return JsonElement.DeepEquals(this.Element, other.Element); + } +} + +class UrlEndpointRequestUrlRewriterImgixConverter + : JsonConverter +{ + public override UrlEndpointRequestUrlRewriterImgix? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return new(JsonSerializer.Deserialize(ref reader, options)); + } + + public override void Write( + Utf8JsonWriter writer, + UrlEndpointRequestUrlRewriterImgix value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Element, options); + } +} + +[JsonConverter(typeof(UrlEndpointRequestUrlRewriterAkamaiConverter))] +public record class UrlEndpointRequestUrlRewriterAkamai +{ + public JsonElement Element { get; private init; } + + public UrlEndpointRequestUrlRewriterAkamai() + { + Element = JsonSerializer.Deserialize( + """ + { + "type": "AKAMAI" + } + """ + ); + } + + internal UrlEndpointRequestUrlRewriterAkamai(JsonElement element) + { + Element = element; + } + + /// + /// Validates that the instance's underlying value is the expected constant. + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public void Validate() + { + if (this != new UrlEndpointRequestUrlRewriterAkamai()) + { + throw new ImageKitInvalidDataException( + "Invalid value given for 'UrlEndpointRequestUrlRewriterAkamai'" + ); + } + } + + public override int GetHashCode() + { + return 0; + } + + public virtual bool Equals(UrlEndpointRequestUrlRewriterAkamai? other) + { + if (other == null) + { + return false; + } + + return JsonElement.DeepEquals(this.Element, other.Element); + } +} + +class UrlEndpointRequestUrlRewriterAkamaiConverter + : JsonConverter +{ + public override UrlEndpointRequestUrlRewriterAkamai? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return new(JsonSerializer.Deserialize(ref reader, options)); + } + + public override void Write( + Utf8JsonWriter writer, + UrlEndpointRequestUrlRewriterAkamai value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Element, options); + } +} diff --git a/src/Imagekit/Models/Accounts/UrlEndpoints/UrlEndpointResponse.cs b/src/Imagekit/Models/Accounts/UrlEndpoints/UrlEndpointResponse.cs new file mode 100644 index 00000000..f859278f --- /dev/null +++ b/src/Imagekit/Models/Accounts/UrlEndpoints/UrlEndpointResponse.cs @@ -0,0 +1,767 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models.Accounts.UrlEndpoints; + +/// +/// URL‑endpoint object as returned by the API. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class UrlEndpointResponse : JsonModel +{ + /// + /// Unique identifier for the URL-endpoint. This is generated by ImageKit when + /// you create a new URL-endpoint. For the default URL-endpoint, this is always `default`. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + /// + /// Description of the URL endpoint. + /// + public required string Description + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("description"); + } + init { this._rawData.Set("description", value); } + } + + /// + /// Ordered list of origin IDs to try when the file isn’t in the Media Library; + /// ImageKit checks them in the sequence provided. Origin must be created before + /// it can be used in a URL endpoint. + /// + public required IReadOnlyList Origins + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct>("origins"); + } + init + { + this._rawData.Set>( + "origins", + ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Path segment appended to your base URL to form the endpoint (letters, digits, + /// and hyphens only — or empty for the default endpoint). + /// + public required string UrlPrefix + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("urlPrefix"); + } + init { this._rawData.Set("urlPrefix", value); } + } + + /// + /// Configuration for third-party URL rewriting. + /// + public UrlEndpointResponseUrlRewriter? UrlRewriter + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("urlRewriter"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("urlRewriter", value); + } + } + + /// + public override void Validate() + { + _ = this.ID; + _ = this.Description; + _ = this.Origins; + _ = this.UrlPrefix; + this.UrlRewriter?.Validate(); + } + + public UrlEndpointResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UrlEndpointResponse(UrlEndpointResponse urlEndpointResponse) + : base(urlEndpointResponse) { } +#pragma warning restore CS8618 + + public UrlEndpointResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UrlEndpointResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UrlEndpointResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class UrlEndpointResponseFromRaw : IFromRawJson +{ + /// + public UrlEndpointResponse FromRawUnchecked(IReadOnlyDictionary rawData) => + UrlEndpointResponse.FromRawUnchecked(rawData); +} + +/// +/// Configuration for third-party URL rewriting. +/// +[JsonConverter(typeof(UrlEndpointResponseUrlRewriterConverter))] +public record class UrlEndpointResponseUrlRewriter : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public UrlEndpointResponseUrlRewriter( + UrlEndpointResponseUrlRewriterCloudinary value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public UrlEndpointResponseUrlRewriter( + UrlEndpointResponseUrlRewriterImgix value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public UrlEndpointResponseUrlRewriter( + UrlEndpointResponseUrlRewriterAkamai value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public UrlEndpointResponseUrlRewriter(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickCloudinary(out var value)) { + /// // `value` is of type `UrlEndpointResponseUrlRewriterCloudinary` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickCloudinary( + [NotNullWhen(true)] out UrlEndpointResponseUrlRewriterCloudinary? value + ) + { + value = this.Value as UrlEndpointResponseUrlRewriterCloudinary; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickImgix(out var value)) { + /// // `value` is of type `UrlEndpointResponseUrlRewriterImgix` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickImgix([NotNullWhen(true)] out UrlEndpointResponseUrlRewriterImgix? value) + { + value = this.Value as UrlEndpointResponseUrlRewriterImgix; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickAkamai(out var value)) { + /// // `value` is of type `UrlEndpointResponseUrlRewriterAkamai` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickAkamai([NotNullWhen(true)] out UrlEndpointResponseUrlRewriterAkamai? value) + { + value = this.Value as UrlEndpointResponseUrlRewriterAkamai; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (UrlEndpointResponseUrlRewriterCloudinary value) => {...}, + /// (UrlEndpointResponseUrlRewriterImgix value) => {...}, + /// (UrlEndpointResponseUrlRewriterAkamai value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action cloudinary, + System::Action imgix, + System::Action akamai + ) + { + switch (this.Value) + { + case UrlEndpointResponseUrlRewriterCloudinary value: + cloudinary(value); + break; + case UrlEndpointResponseUrlRewriterImgix value: + imgix(value); + break; + case UrlEndpointResponseUrlRewriterAkamai value: + akamai(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of UrlEndpointResponseUrlRewriter" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (UrlEndpointResponseUrlRewriterCloudinary value) => {...}, + /// (UrlEndpointResponseUrlRewriterImgix value) => {...}, + /// (UrlEndpointResponseUrlRewriterAkamai value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func cloudinary, + System::Func imgix, + System::Func akamai + ) + { + return this.Value switch + { + UrlEndpointResponseUrlRewriterCloudinary value => cloudinary(value), + UrlEndpointResponseUrlRewriterImgix value => imgix(value), + UrlEndpointResponseUrlRewriterAkamai value => akamai(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of UrlEndpointResponseUrlRewriter" + ), + }; + } + + public static implicit operator UrlEndpointResponseUrlRewriter( + UrlEndpointResponseUrlRewriterCloudinary value + ) => new(value); + + public static implicit operator UrlEndpointResponseUrlRewriter( + UrlEndpointResponseUrlRewriterImgix value + ) => new(value); + + public static implicit operator UrlEndpointResponseUrlRewriter( + UrlEndpointResponseUrlRewriterAkamai value + ) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of UrlEndpointResponseUrlRewriter" + ); + } + this.Switch( + (cloudinary) => cloudinary.Validate(), + (imgix) => imgix.Validate(), + (akamai) => akamai.Validate() + ); + } + + public virtual bool Equals(UrlEndpointResponseUrlRewriter? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + UrlEndpointResponseUrlRewriterCloudinary _ => 0, + UrlEndpointResponseUrlRewriterImgix _ => 1, + UrlEndpointResponseUrlRewriterAkamai _ => 2, + _ => -1, + }; + } +} + +sealed class UrlEndpointResponseUrlRewriterConverter : JsonConverter +{ + public override UrlEndpointResponseUrlRewriter? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + string? type; + try + { + type = element.GetProperty("type").GetString(); + } + catch + { + type = null; + } + + switch (type) + { + case "CLOUDINARY": + { + try + { + var deserialized = + JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "IMGIX": + { + try + { + var deserialized = + JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "AKAMAI": + { + try + { + var deserialized = + JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + default: + { + return new UrlEndpointResponseUrlRewriter(element); + } + } + } + + public override void Write( + Utf8JsonWriter writer, + UrlEndpointResponseUrlRewriter value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter( + typeof(JsonModelConverter< + UrlEndpointResponseUrlRewriterCloudinary, + UrlEndpointResponseUrlRewriterCloudinaryFromRaw + >) +)] +public sealed record class UrlEndpointResponseUrlRewriterCloudinary : JsonModel +{ + /// + /// Whether to preserve `<asset_type>/<delivery_type>` in the rewritten URL. + /// + public required bool PreserveAssetDeliveryTypes + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("preserveAssetDeliveryTypes"); + } + init { this._rawData.Set("preserveAssetDeliveryTypes", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + public override void Validate() + { + _ = this.PreserveAssetDeliveryTypes; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("CLOUDINARY"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + } + + public UrlEndpointResponseUrlRewriterCloudinary() + { + this.Type = JsonSerializer.SerializeToElement("CLOUDINARY"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UrlEndpointResponseUrlRewriterCloudinary( + UrlEndpointResponseUrlRewriterCloudinary urlEndpointResponseUrlRewriterCloudinary + ) + : base(urlEndpointResponseUrlRewriterCloudinary) { } +#pragma warning restore CS8618 + + public UrlEndpointResponseUrlRewriterCloudinary( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("CLOUDINARY"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UrlEndpointResponseUrlRewriterCloudinary(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UrlEndpointResponseUrlRewriterCloudinary FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public UrlEndpointResponseUrlRewriterCloudinary(bool preserveAssetDeliveryTypes) + : this() + { + this.PreserveAssetDeliveryTypes = preserveAssetDeliveryTypes; + } +} + +class UrlEndpointResponseUrlRewriterCloudinaryFromRaw + : IFromRawJson +{ + /// + public UrlEndpointResponseUrlRewriterCloudinary FromRawUnchecked( + IReadOnlyDictionary rawData + ) => UrlEndpointResponseUrlRewriterCloudinary.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(UrlEndpointResponseUrlRewriterImgixConverter))] +public record class UrlEndpointResponseUrlRewriterImgix +{ + public JsonElement Element { get; private init; } + + public UrlEndpointResponseUrlRewriterImgix() + { + Element = JsonSerializer.Deserialize( + """ + { + "type": "IMGIX" + } + """ + ); + } + + internal UrlEndpointResponseUrlRewriterImgix(JsonElement element) + { + Element = element; + } + + /// + /// Validates that the instance's underlying value is the expected constant. + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public void Validate() + { + if (this != new UrlEndpointResponseUrlRewriterImgix()) + { + throw new ImageKitInvalidDataException( + "Invalid value given for 'UrlEndpointResponseUrlRewriterImgix'" + ); + } + } + + public override int GetHashCode() + { + return 0; + } + + public virtual bool Equals(UrlEndpointResponseUrlRewriterImgix? other) + { + if (other == null) + { + return false; + } + + return JsonElement.DeepEquals(this.Element, other.Element); + } +} + +class UrlEndpointResponseUrlRewriterImgixConverter + : JsonConverter +{ + public override UrlEndpointResponseUrlRewriterImgix? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return new(JsonSerializer.Deserialize(ref reader, options)); + } + + public override void Write( + Utf8JsonWriter writer, + UrlEndpointResponseUrlRewriterImgix value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Element, options); + } +} + +[JsonConverter(typeof(UrlEndpointResponseUrlRewriterAkamaiConverter))] +public record class UrlEndpointResponseUrlRewriterAkamai +{ + public JsonElement Element { get; private init; } + + public UrlEndpointResponseUrlRewriterAkamai() + { + Element = JsonSerializer.Deserialize( + """ + { + "type": "AKAMAI" + } + """ + ); + } + + internal UrlEndpointResponseUrlRewriterAkamai(JsonElement element) + { + Element = element; + } + + /// + /// Validates that the instance's underlying value is the expected constant. + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public void Validate() + { + if (this != new UrlEndpointResponseUrlRewriterAkamai()) + { + throw new ImageKitInvalidDataException( + "Invalid value given for 'UrlEndpointResponseUrlRewriterAkamai'" + ); + } + } + + public override int GetHashCode() + { + return 0; + } + + public virtual bool Equals(UrlEndpointResponseUrlRewriterAkamai? other) + { + if (other == null) + { + return false; + } + + return JsonElement.DeepEquals(this.Element, other.Element); + } +} + +class UrlEndpointResponseUrlRewriterAkamaiConverter + : JsonConverter +{ + public override UrlEndpointResponseUrlRewriterAkamai? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return new(JsonSerializer.Deserialize(ref reader, options)); + } + + public override void Write( + Utf8JsonWriter writer, + UrlEndpointResponseUrlRewriterAkamai value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Element, options); + } +} diff --git a/src/Imagekit/Models/Accounts/UrlEndpoints/UrlEndpointUpdateParams.cs b/src/Imagekit/Models/Accounts/UrlEndpoints/UrlEndpointUpdateParams.cs new file mode 100644 index 00000000..05d07504 --- /dev/null +++ b/src/Imagekit/Models/Accounts/UrlEndpoints/UrlEndpointUpdateParams.cs @@ -0,0 +1,860 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; +using Text = System.Text; + +namespace Imagekit.Models.Accounts.UrlEndpoints; + +/// +/// **Note:** This API is currently in beta. Updates the URL‑endpoint identified +/// by `id` and returns the updated object. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class UrlEndpointUpdateParams : ParamsBase +{ + readonly JsonDictionary _rawBodyData = new(); + public IReadOnlyDictionary RawBodyData + { + get { return this._rawBodyData.Freeze(); } + } + + public string? ID { get; init; } + + /// + /// Description of the URL endpoint. + /// + public required string Description + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("description"); + } + init { this._rawBodyData.Set("description", value); } + } + + /// + /// Ordered list of origin IDs to try when the file isn’t in the Media Library; + /// ImageKit checks them in the sequence provided. Origin must be created before + /// it can be used in a URL endpoint. + /// + public IReadOnlyList? Origins + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct>("origins"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set?>( + "origins", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Path segment appended to your base URL to form the endpoint (letters, digits, + /// and hyphens only — or empty for the default endpoint). + /// + public string? UrlPrefix + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("urlPrefix"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("urlPrefix", value); + } + } + + /// + /// Configuration for third-party URL rewriting. + /// + public UrlEndpointUpdateParamsUrlRewriter? UrlRewriter + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass( + "urlRewriter" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("urlRewriter", value); + } + } + + public UrlEndpointUpdateParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UrlEndpointUpdateParams(UrlEndpointUpdateParams urlEndpointUpdateParams) + : base(urlEndpointUpdateParams) + { + this.ID = urlEndpointUpdateParams.ID; + + this._rawBodyData = new(urlEndpointUpdateParams._rawBodyData); + } +#pragma warning restore CS8618 + + public UrlEndpointUpdateParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UrlEndpointUpdateParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + FrozenDictionary rawBodyData, + string id + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + this.ID = id; + } +#pragma warning restore CS8618 + + /// + public static UrlEndpointUpdateParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData, + string id + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + FrozenDictionary.ToFrozenDictionary(rawBodyData), + id + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["ID"] = JsonSerializer.SerializeToElement(this.ID), + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this._rawBodyData.Freeze()), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(UrlEndpointUpdateParams? other) + { + if (other == null) + { + return false; + } + return (this.ID?.Equals(other.ID) ?? other.ID == null) + && this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this._rawBodyData.Equals(other._rawBodyData); + } + + public override System::Uri Url(ClientOptions options) + { + return new System::UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + + string.Format("/v1/accounts/url-endpoints/{0}", this.ID) + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} + +/// +/// Configuration for third-party URL rewriting. +/// +[JsonConverter(typeof(UrlEndpointUpdateParamsUrlRewriterConverter))] +public record class UrlEndpointUpdateParamsUrlRewriter : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public UrlEndpointUpdateParamsUrlRewriter( + UrlEndpointUpdateParamsUrlRewriterCloudinary value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public UrlEndpointUpdateParamsUrlRewriter( + UrlEndpointUpdateParamsUrlRewriterImgix value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public UrlEndpointUpdateParamsUrlRewriter( + UrlEndpointUpdateParamsUrlRewriterAkamai value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public UrlEndpointUpdateParamsUrlRewriter(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickCloudinary(out var value)) { + /// // `value` is of type `UrlEndpointUpdateParamsUrlRewriterCloudinary` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickCloudinary( + [NotNullWhen(true)] out UrlEndpointUpdateParamsUrlRewriterCloudinary? value + ) + { + value = this.Value as UrlEndpointUpdateParamsUrlRewriterCloudinary; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickImgix(out var value)) { + /// // `value` is of type `UrlEndpointUpdateParamsUrlRewriterImgix` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickImgix([NotNullWhen(true)] out UrlEndpointUpdateParamsUrlRewriterImgix? value) + { + value = this.Value as UrlEndpointUpdateParamsUrlRewriterImgix; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickAkamai(out var value)) { + /// // `value` is of type `UrlEndpointUpdateParamsUrlRewriterAkamai` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickAkamai( + [NotNullWhen(true)] out UrlEndpointUpdateParamsUrlRewriterAkamai? value + ) + { + value = this.Value as UrlEndpointUpdateParamsUrlRewriterAkamai; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (UrlEndpointUpdateParamsUrlRewriterCloudinary value) => {...}, + /// (UrlEndpointUpdateParamsUrlRewriterImgix value) => {...}, + /// (UrlEndpointUpdateParamsUrlRewriterAkamai value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action cloudinary, + System::Action imgix, + System::Action akamai + ) + { + switch (this.Value) + { + case UrlEndpointUpdateParamsUrlRewriterCloudinary value: + cloudinary(value); + break; + case UrlEndpointUpdateParamsUrlRewriterImgix value: + imgix(value); + break; + case UrlEndpointUpdateParamsUrlRewriterAkamai value: + akamai(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of UrlEndpointUpdateParamsUrlRewriter" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (UrlEndpointUpdateParamsUrlRewriterCloudinary value) => {...}, + /// (UrlEndpointUpdateParamsUrlRewriterImgix value) => {...}, + /// (UrlEndpointUpdateParamsUrlRewriterAkamai value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func cloudinary, + System::Func imgix, + System::Func akamai + ) + { + return this.Value switch + { + UrlEndpointUpdateParamsUrlRewriterCloudinary value => cloudinary(value), + UrlEndpointUpdateParamsUrlRewriterImgix value => imgix(value), + UrlEndpointUpdateParamsUrlRewriterAkamai value => akamai(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of UrlEndpointUpdateParamsUrlRewriter" + ), + }; + } + + public static implicit operator UrlEndpointUpdateParamsUrlRewriter( + UrlEndpointUpdateParamsUrlRewriterCloudinary value + ) => new(value); + + public static implicit operator UrlEndpointUpdateParamsUrlRewriter( + UrlEndpointUpdateParamsUrlRewriterImgix value + ) => new(value); + + public static implicit operator UrlEndpointUpdateParamsUrlRewriter( + UrlEndpointUpdateParamsUrlRewriterAkamai value + ) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of UrlEndpointUpdateParamsUrlRewriter" + ); + } + this.Switch( + (cloudinary) => cloudinary.Validate(), + (imgix) => imgix.Validate(), + (akamai) => akamai.Validate() + ); + } + + public virtual bool Equals(UrlEndpointUpdateParamsUrlRewriter? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + UrlEndpointUpdateParamsUrlRewriterCloudinary _ => 0, + UrlEndpointUpdateParamsUrlRewriterImgix _ => 1, + UrlEndpointUpdateParamsUrlRewriterAkamai _ => 2, + _ => -1, + }; + } +} + +sealed class UrlEndpointUpdateParamsUrlRewriterConverter + : JsonConverter +{ + public override UrlEndpointUpdateParamsUrlRewriter? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + string? type; + try + { + type = element.GetProperty("type").GetString(); + } + catch + { + type = null; + } + + switch (type) + { + case "CLOUDINARY": + { + try + { + var deserialized = + JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "IMGIX": + { + try + { + var deserialized = + JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "AKAMAI": + { + try + { + var deserialized = + JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + default: + { + return new UrlEndpointUpdateParamsUrlRewriter(element); + } + } + } + + public override void Write( + Utf8JsonWriter writer, + UrlEndpointUpdateParamsUrlRewriter value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter( + typeof(JsonModelConverter< + UrlEndpointUpdateParamsUrlRewriterCloudinary, + UrlEndpointUpdateParamsUrlRewriterCloudinaryFromRaw + >) +)] +public sealed record class UrlEndpointUpdateParamsUrlRewriterCloudinary : JsonModel +{ + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Whether to preserve `<asset_type>/<delivery_type>` in the rewritten URL. + /// + public bool? PreserveAssetDeliveryTypes + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("preserveAssetDeliveryTypes"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("preserveAssetDeliveryTypes", value); + } + } + + /// + public override void Validate() + { + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("CLOUDINARY"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.PreserveAssetDeliveryTypes; + } + + public UrlEndpointUpdateParamsUrlRewriterCloudinary() + { + this.Type = JsonSerializer.SerializeToElement("CLOUDINARY"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UrlEndpointUpdateParamsUrlRewriterCloudinary( + UrlEndpointUpdateParamsUrlRewriterCloudinary urlEndpointUpdateParamsUrlRewriterCloudinary + ) + : base(urlEndpointUpdateParamsUrlRewriterCloudinary) { } +#pragma warning restore CS8618 + + public UrlEndpointUpdateParamsUrlRewriterCloudinary( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("CLOUDINARY"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UrlEndpointUpdateParamsUrlRewriterCloudinary(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UrlEndpointUpdateParamsUrlRewriterCloudinary FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class UrlEndpointUpdateParamsUrlRewriterCloudinaryFromRaw + : IFromRawJson +{ + /// + public UrlEndpointUpdateParamsUrlRewriterCloudinary FromRawUnchecked( + IReadOnlyDictionary rawData + ) => UrlEndpointUpdateParamsUrlRewriterCloudinary.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(UrlEndpointUpdateParamsUrlRewriterImgixConverter))] +public record class UrlEndpointUpdateParamsUrlRewriterImgix +{ + public JsonElement Element { get; private init; } + + public UrlEndpointUpdateParamsUrlRewriterImgix() + { + Element = JsonSerializer.Deserialize( + """ + { + "type": "IMGIX" + } + """ + ); + } + + internal UrlEndpointUpdateParamsUrlRewriterImgix(JsonElement element) + { + Element = element; + } + + /// + /// Validates that the instance's underlying value is the expected constant. + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public void Validate() + { + if (this != new UrlEndpointUpdateParamsUrlRewriterImgix()) + { + throw new ImageKitInvalidDataException( + "Invalid value given for 'UrlEndpointUpdateParamsUrlRewriterImgix'" + ); + } + } + + public override int GetHashCode() + { + return 0; + } + + public virtual bool Equals(UrlEndpointUpdateParamsUrlRewriterImgix? other) + { + if (other == null) + { + return false; + } + + return JsonElement.DeepEquals(this.Element, other.Element); + } +} + +class UrlEndpointUpdateParamsUrlRewriterImgixConverter + : JsonConverter +{ + public override UrlEndpointUpdateParamsUrlRewriterImgix? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return new(JsonSerializer.Deserialize(ref reader, options)); + } + + public override void Write( + Utf8JsonWriter writer, + UrlEndpointUpdateParamsUrlRewriterImgix value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Element, options); + } +} + +[JsonConverter(typeof(UrlEndpointUpdateParamsUrlRewriterAkamaiConverter))] +public record class UrlEndpointUpdateParamsUrlRewriterAkamai +{ + public JsonElement Element { get; private init; } + + public UrlEndpointUpdateParamsUrlRewriterAkamai() + { + Element = JsonSerializer.Deserialize( + """ + { + "type": "AKAMAI" + } + """ + ); + } + + internal UrlEndpointUpdateParamsUrlRewriterAkamai(JsonElement element) + { + Element = element; + } + + /// + /// Validates that the instance's underlying value is the expected constant. + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public void Validate() + { + if (this != new UrlEndpointUpdateParamsUrlRewriterAkamai()) + { + throw new ImageKitInvalidDataException( + "Invalid value given for 'UrlEndpointUpdateParamsUrlRewriterAkamai'" + ); + } + } + + public override int GetHashCode() + { + return 0; + } + + public virtual bool Equals(UrlEndpointUpdateParamsUrlRewriterAkamai? other) + { + if (other == null) + { + return false; + } + + return JsonElement.DeepEquals(this.Element, other.Element); + } +} + +class UrlEndpointUpdateParamsUrlRewriterAkamaiConverter + : JsonConverter +{ + public override UrlEndpointUpdateParamsUrlRewriterAkamai? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return new(JsonSerializer.Deserialize(ref reader, options)); + } + + public override void Write( + Utf8JsonWriter writer, + UrlEndpointUpdateParamsUrlRewriterAkamai value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Element, options); + } +} diff --git a/src/Imagekit/Models/Accounts/Usage/UsageGetParams.cs b/src/Imagekit/Models/Accounts/Usage/UsageGetParams.cs new file mode 100644 index 00000000..bea20afe --- /dev/null +++ b/src/Imagekit/Models/Accounts/Usage/UsageGetParams.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Models.Accounts.Usage; + +/// +/// Get the account usage information between two dates. Note that the API response +/// includes data from the start date while excluding data from the end date. In other +/// words, the data covers the period starting from the specified start date up to, +/// but not including, the end date. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class UsageGetParams : ParamsBase +{ + /// + /// Specify a `endDate` in `YYYY-MM-DD` format. It should be after the `startDate`. + /// The difference between `startDate` and `endDate` should be less than 90 days. + /// + public required string EndDate + { + get + { + this._rawQueryData.Freeze(); + return this._rawQueryData.GetNotNullClass("endDate"); + } + init { this._rawQueryData.Set("endDate", value); } + } + + /// + /// Specify a `startDate` in `YYYY-MM-DD` format. It should be before the `endDate`. + /// The difference between `startDate` and `endDate` should be less than 90 days. + /// + public required string StartDate + { + get + { + this._rawQueryData.Freeze(); + return this._rawQueryData.GetNotNullClass("startDate"); + } + init { this._rawQueryData.Set("startDate", value); } + } + + public UsageGetParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UsageGetParams(UsageGetParams usageGetParams) + : base(usageGetParams) { } +#pragma warning restore CS8618 + + public UsageGetParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UsageGetParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } +#pragma warning restore CS8618 + + /// + public static UsageGetParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(UsageGetParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder(options.BaseUrl.ToString().TrimEnd('/') + "/v1/accounts/usage") + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Accounts/Usage/UsageGetResponse.cs b/src/Imagekit/Models/Accounts/Usage/UsageGetResponse.cs new file mode 100644 index 00000000..4481d2dd --- /dev/null +++ b/src/Imagekit/Models/Accounts/Usage/UsageGetResponse.cs @@ -0,0 +1,163 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models.Accounts.Usage; + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class UsageGetResponse : JsonModel +{ + /// + /// Amount of bandwidth used in bytes. + /// + public long? BandwidthBytes + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("bandwidthBytes"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("bandwidthBytes", value); + } + } + + /// + /// Number of extension units used. + /// + public long? ExtensionUnitsCount + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("extensionUnitsCount"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("extensionUnitsCount", value); + } + } + + /// + /// Storage used by media library in bytes. + /// + public long? MediaLibraryStorageBytes + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("mediaLibraryStorageBytes"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("mediaLibraryStorageBytes", value); + } + } + + /// + /// Storage used by the original cache in bytes. + /// + public long? OriginalCacheStorageBytes + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("originalCacheStorageBytes"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("originalCacheStorageBytes", value); + } + } + + /// + /// Number of video processing units used. + /// + public long? VideoProcessingUnitsCount + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("videoProcessingUnitsCount"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("videoProcessingUnitsCount", value); + } + } + + /// + public override void Validate() + { + _ = this.BandwidthBytes; + _ = this.ExtensionUnitsCount; + _ = this.MediaLibraryStorageBytes; + _ = this.OriginalCacheStorageBytes; + _ = this.VideoProcessingUnitsCount; + } + + public UsageGetResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UsageGetResponse(UsageGetResponse usageGetResponse) + : base(usageGetResponse) { } +#pragma warning restore CS8618 + + public UsageGetResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UsageGetResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UsageGetResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class UsageGetResponseFromRaw : IFromRawJson +{ + /// + public UsageGetResponse FromRawUnchecked(IReadOnlyDictionary rawData) => + UsageGetResponse.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Assets/AssetListParams.cs b/src/Imagekit/Models/Assets/AssetListParams.cs new file mode 100644 index 00000000..7d458b5e --- /dev/null +++ b/src/Imagekit/Models/Assets/AssetListParams.cs @@ -0,0 +1,469 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models.Assets; + +/// +/// This API can list all the uploaded files and folders in your ImageKit.io media +/// library. In addition, you can fine-tune your query by specifying various filters +/// by generating a query string in a Lucene-like syntax and provide this generated +/// string as the value of the `searchQuery`. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class AssetListParams : ParamsBase +{ + /// + /// Filter results by file type. + /// + /// - `all` — include all file types - `image` — include only image files + /// - `non-image` — include only non-image files (e.g., JS, CSS, video) + /// + public ApiEnum? FileType + { + get + { + this._rawQueryData.Freeze(); + return this._rawQueryData.GetNullableClass>("fileType"); + } + init + { + if (value == null) + { + return; + } + + this._rawQueryData.Set("fileType", value); + } + } + + /// + /// The maximum number of results to return in response. + /// + public long? Limit + { + get + { + this._rawQueryData.Freeze(); + return this._rawQueryData.GetNullableStruct("limit"); + } + init + { + if (value == null) + { + return; + } + + this._rawQueryData.Set("limit", value); + } + } + + /// + /// Folder path if you want to limit the search within a specific folder. For + /// example, `/sales-banner/` will only search in folder sales-banner. + /// + /// Note : If your use case involves searching within a folder as well as + /// its subfolders, you can use `path` parameter in `searchQuery` with appropriate + /// operator. Checkout [Supported parameters](/docs/api-reference/digital-asset-management-dam/list-and-search-assets#supported-parameters) + /// for more information. + /// + public string? Path + { + get + { + this._rawQueryData.Freeze(); + return this._rawQueryData.GetNullableClass("path"); + } + init + { + if (value == null) + { + return; + } + + this._rawQueryData.Set("path", value); + } + } + + /// + /// Query string in a Lucene-like query language e.g. `createdAt > "7d"`. + /// + /// Note : When the searchQuery parameter is present, the following query + /// parameters will have no effect on the result: + /// + /// 1. `tags` 2. `type` 3. `name` + /// + /// [Learn more](/docs/api-reference/digital-asset-management-dam/list-and-search-assets#advanced-search-queries) + /// from examples. + /// + public string? SearchQuery + { + get + { + this._rawQueryData.Freeze(); + return this._rawQueryData.GetNullableClass("searchQuery"); + } + init + { + if (value == null) + { + return; + } + + this._rawQueryData.Set("searchQuery", value); + } + } + + /// + /// The number of results to skip before returning results. + /// + public long? Skip + { + get + { + this._rawQueryData.Freeze(); + return this._rawQueryData.GetNullableStruct("skip"); + } + init + { + if (value == null) + { + return; + } + + this._rawQueryData.Set("skip", value); + } + } + + /// + /// Sort the results by one of the supported fields in ascending or descending + /// order. + /// + public ApiEnum? Sort + { + get + { + this._rawQueryData.Freeze(); + return this._rawQueryData.GetNullableClass>("sort"); + } + init + { + if (value == null) + { + return; + } + + this._rawQueryData.Set("sort", value); + } + } + + /// + /// Filter results by asset type. + /// + /// - `file` — returns only files - `file-version` — returns specific + /// file versions - `folder` — returns only folders - `all` — returns both + /// files and folders (excludes `file-version`) + /// + public ApiEnum? Type + { + get + { + this._rawQueryData.Freeze(); + return this._rawQueryData.GetNullableClass< + ApiEnum + >("type"); + } + init + { + if (value == null) + { + return; + } + + this._rawQueryData.Set("type", value); + } + } + + public AssetListParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public AssetListParams(AssetListParams assetListParams) + : base(assetListParams) { } +#pragma warning restore CS8618 + + public AssetListParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + AssetListParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } +#pragma warning restore CS8618 + + /// + public static AssetListParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(AssetListParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override System::Uri Url(ClientOptions options) + { + return new System::UriBuilder(options.BaseUrl.ToString().TrimEnd('/') + "/v1/files") + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} + +/// +/// Filter results by file type. +/// +/// - `all` — include all file types - `image` — include only image files +/// - `non-image` — include only non-image files (e.g., JS, CSS, video) +/// +[JsonConverter(typeof(FileTypeConverter))] +public enum FileType +{ + All, + Image, + NonImage, +} + +sealed class FileTypeConverter : JsonConverter +{ + public override FileType Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "all" => FileType.All, + "image" => FileType.Image, + "non-image" => FileType.NonImage, + _ => (FileType)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, FileType value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + FileType.All => "all", + FileType.Image => "image", + FileType.NonImage => "non-image", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Sort the results by one of the supported fields in ascending or descending order. +/// +[JsonConverter(typeof(SortConverter))] +public enum Sort +{ + AscName, + DescName, + AscCreated, + DescCreated, + AscUpdated, + DescUpdated, + AscHeight, + DescHeight, + AscWidth, + DescWidth, + AscSize, + DescSize, + AscRelevance, + DescRelevance, +} + +sealed class SortConverter : JsonConverter +{ + public override Sort Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "ASC_NAME" => Sort.AscName, + "DESC_NAME" => Sort.DescName, + "ASC_CREATED" => Sort.AscCreated, + "DESC_CREATED" => Sort.DescCreated, + "ASC_UPDATED" => Sort.AscUpdated, + "DESC_UPDATED" => Sort.DescUpdated, + "ASC_HEIGHT" => Sort.AscHeight, + "DESC_HEIGHT" => Sort.DescHeight, + "ASC_WIDTH" => Sort.AscWidth, + "DESC_WIDTH" => Sort.DescWidth, + "ASC_SIZE" => Sort.AscSize, + "DESC_SIZE" => Sort.DescSize, + "ASC_RELEVANCE" => Sort.AscRelevance, + "DESC_RELEVANCE" => Sort.DescRelevance, + _ => (Sort)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, Sort value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + Sort.AscName => "ASC_NAME", + Sort.DescName => "DESC_NAME", + Sort.AscCreated => "ASC_CREATED", + Sort.DescCreated => "DESC_CREATED", + Sort.AscUpdated => "ASC_UPDATED", + Sort.DescUpdated => "DESC_UPDATED", + Sort.AscHeight => "ASC_HEIGHT", + Sort.DescHeight => "DESC_HEIGHT", + Sort.AscWidth => "ASC_WIDTH", + Sort.DescWidth => "DESC_WIDTH", + Sort.AscSize => "ASC_SIZE", + Sort.DescSize => "DESC_SIZE", + Sort.AscRelevance => "ASC_RELEVANCE", + Sort.DescRelevance => "DESC_RELEVANCE", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Filter results by asset type. +/// +/// - `file` — returns only files - `file-version` — returns specific file +/// versions - `folder` — returns only folders - `all` — returns both files and +/// folders (excludes `file-version`) +/// +[JsonConverter(typeof(TypeConverter))] +public enum Type +{ + File, + FileVersion, + Folder, + All, +} + +sealed class TypeConverter : JsonConverter +{ + public override global::Imagekit.Models.Assets.Type Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "file" => global::Imagekit.Models.Assets.Type.File, + "file-version" => global::Imagekit.Models.Assets.Type.FileVersion, + "folder" => global::Imagekit.Models.Assets.Type.Folder, + "all" => global::Imagekit.Models.Assets.Type.All, + _ => (global::Imagekit.Models.Assets.Type)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + global::Imagekit.Models.Assets.Type value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + global::Imagekit.Models.Assets.Type.File => "file", + global::Imagekit.Models.Assets.Type.FileVersion => "file-version", + global::Imagekit.Models.Assets.Type.Folder => "folder", + global::Imagekit.Models.Assets.Type.All => "all", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} diff --git a/src/Imagekit/Models/Assets/AssetListResponse.cs b/src/Imagekit/Models/Assets/AssetListResponse.cs new file mode 100644 index 00000000..5548d1f7 --- /dev/null +++ b/src/Imagekit/Models/Assets/AssetListResponse.cs @@ -0,0 +1,306 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Files; +using System = System; + +namespace Imagekit.Models.Assets; + +/// +/// Object containing details of a file or file version. +/// +[JsonConverter(typeof(AssetListResponseConverter))] +public record class AssetListResponse : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public System::DateTimeOffset? CreatedAt + { + get + { + return Match( + file: (x) => x.CreatedAt, + folder: (x) => x.CreatedAt + ); + } + } + + public string? Name + { + get { return Match(file: (x) => x.Name, folder: (x) => x.Name); } + } + + public System::DateTimeOffset? UpdatedAt + { + get + { + return Match( + file: (x) => x.UpdatedAt, + folder: (x) => x.UpdatedAt + ); + } + } + + public AssetListResponse(File value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public AssetListResponse(Folder value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public AssetListResponse(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickFile(out var value)) { + /// // `value` is of type `File` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickFile([NotNullWhen(true)] out File? value) + { + value = this.Value as File; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickFolder(out var value)) { + /// // `value` is of type `Folder` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickFolder([NotNullWhen(true)] out Folder? value) + { + value = this.Value as Folder; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (File value) => {...}, + /// (Folder value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action file, System::Action folder) + { + switch (this.Value) + { + case File value: + file(value); + break; + case Folder value: + folder(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of AssetListResponse" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (File value) => {...}, + /// (Folder value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func file, System::Func folder) + { + return this.Value switch + { + File value => file(value), + Folder value => folder(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of AssetListResponse" + ), + }; + } + + public static implicit operator AssetListResponse(File value) => new(value); + + public static implicit operator AssetListResponse(Folder value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of AssetListResponse" + ); + } + this.Switch((file) => file.Validate(), (folder) => folder.Validate()); + } + + public virtual bool Equals(AssetListResponse? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + File _ => 0, + Folder _ => 1, + _ => -1, + }; + } +} + +sealed class AssetListResponseConverter : JsonConverter +{ + public override AssetListResponse? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + string? type; + try + { + type = element.GetProperty("type").GetString(); + } + catch + { + type = null; + } + + switch (type) + { + case "folder": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + default: + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + } + } + + public override void Write( + Utf8JsonWriter writer, + AssetListResponse value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} diff --git a/src/Imagekit/Models/BaseOverlay.cs b/src/Imagekit/Models/BaseOverlay.cs new file mode 100644 index 00000000..38ef7ef5 --- /dev/null +++ b/src/Imagekit/Models/BaseOverlay.cs @@ -0,0 +1,203 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models; + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class BaseOverlay : JsonModel +{ + /// + /// Controls how the layer blends with the base image or underlying content. + /// Maps to `lm` in the URL. By default, layers completely cover the base image + /// beneath them. Layer modes change this behavior: - `multiply`: Multiplies + /// the pixel values of the layer with the base image. The result is always darker + /// than the original images. This is ideal for applying shadows or color tints. + /// - `displace`: Uses the layer as a displacement map to distort pixels in the + /// base image. The red channel controls horizontal displacement, and the green + /// channel controls vertical displacement. Requires `x` or `y` parameter to control + /// displacement magnitude. - `cutout`: Acts as an inverse mask where opaque areas + /// of the layer turn the base image transparent, while transparent areas leave + /// the base image unchanged. This mode functions like a hole-punch, effectively + /// cutting the shape of the layer out of the underlying image. - `cutter`: Acts + /// as a shape mask where only the parts of the base image that fall inside the + /// opaque area of the layer are preserved. This mode functions like a cookie-cutter, + /// trimming the base image to match the specific dimensions and shape of the + /// layer. See [Layer modes](https://imagekit.io/docs/add-overlays-on-images#layer-modes). + /// + public ApiEnum? LayerMode + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("layerMode"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("layerMode", value); + } + } + + /// + /// Specifies the overlay's position relative to the parent asset. See [Position + /// of Layer](https://imagekit.io/docs/transformations#position-of-layer). + /// + public OverlayPosition? Position + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("position"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("position", value); + } + } + + /// + /// Specifies timing information for the overlay (only applicable if the base + /// asset is a video). See [Position of Layer](https://imagekit.io/docs/transformations#position-of-layer). + /// + public OverlayTiming? Timing + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("timing"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("timing", value); + } + } + + /// + public override void Validate() + { + this.LayerMode?.Validate(); + this.Position?.Validate(); + this.Timing?.Validate(); + } + + public BaseOverlay() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public BaseOverlay(BaseOverlay baseOverlay) + : base(baseOverlay) { } +#pragma warning restore CS8618 + + public BaseOverlay(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + BaseOverlay(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static BaseOverlay FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class BaseOverlayFromRaw : IFromRawJson +{ + /// + public BaseOverlay FromRawUnchecked(IReadOnlyDictionary rawData) => + BaseOverlay.FromRawUnchecked(rawData); +} + +/// +/// Controls how the layer blends with the base image or underlying content. Maps +/// to `lm` in the URL. By default, layers completely cover the base image beneath +/// them. Layer modes change this behavior: - `multiply`: Multiplies the pixel values +/// of the layer with the base image. The result is always darker than the original +/// images. This is ideal for applying shadows or color tints. - `displace`: Uses +/// the layer as a displacement map to distort pixels in the base image. The red +/// channel controls horizontal displacement, and the green channel controls vertical +/// displacement. Requires `x` or `y` parameter to control displacement magnitude. +/// - `cutout`: Acts as an inverse mask where opaque areas of the layer turn the base +/// image transparent, while transparent areas leave the base image unchanged. This +/// mode functions like a hole-punch, effectively cutting the shape of the layer out +/// of the underlying image. - `cutter`: Acts as a shape mask where only the parts +/// of the base image that fall inside the opaque area of the layer are preserved. +/// This mode functions like a cookie-cutter, trimming the base image to match the +/// specific dimensions and shape of the layer. See [Layer modes](https://imagekit.io/docs/add-overlays-on-images#layer-modes). +/// +[JsonConverter(typeof(LayerModeConverter))] +public enum LayerMode +{ + Multiply, + Cutter, + Cutout, + Displace, +} + +sealed class LayerModeConverter : JsonConverter +{ + public override LayerMode Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "multiply" => LayerMode.Multiply, + "cutter" => LayerMode.Cutter, + "cutout" => LayerMode.Cutout, + "displace" => LayerMode.Displace, + _ => (LayerMode)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + LayerMode value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + LayerMode.Multiply => "multiply", + LayerMode.Cutter => "cutter", + LayerMode.Cutout => "cutout", + LayerMode.Displace => "displace", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} diff --git a/src/Imagekit/Models/Beta/V2/Files/FileUploadParams.cs b/src/Imagekit/Models/Beta/V2/Files/FileUploadParams.cs new file mode 100644 index 00000000..e015d066 --- /dev/null +++ b/src/Imagekit/Models/Beta/V2/Files/FileUploadParams.cs @@ -0,0 +1,1622 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Helper; +using System = System; + +namespace Imagekit.Models.Beta.V2.Files; + +/// +/// The V2 API enhances security by verifying the entire payload using JWT. This API +/// is in beta. +/// +/// ImageKit.io allows you to upload files directly from both the server and +/// client sides. For server-side uploads, private API key authentication is used. +/// For client-side uploads, generate a one-time `token` from your secure backend +/// using private API. [Learn more](/docs/api-reference/upload-file/upload-file-v2#how-to-implement-secure-client-side-file-upload) +/// about how to implement secure client-side file upload. +/// +/// **File size limit** \ On the free plan, the maximum upload file sizes are +/// 25MB for images, audio, and raw files, and 100MB for videos. On the Lite paid +/// plan, these limits increase to 40MB for images, audio, and raw files and 300MB +/// for videos, whereas on the Pro paid plan, these limits increase to 50MB for images, +/// audio, and raw files and 2GB for videos. These limits can be further increased +/// with enterprise plans. +/// +/// **Version limit** \ A file can have a maximum of 100 versions. +/// +/// **Demo applications** +/// +/// - A full-fledged [upload widget using Uppy](https://github.com/imagekit-samples/uppy-uploader), +/// supporting file selections from local storage, URL, Dropbox, Google Drive, Instagram, +/// and more. - [Quick start guides](/docs/quick-start-guides) for various frameworks +/// and technologies. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class FileUploadParams : ParamsBase +{ + readonly MultipartJsonDictionary _rawBodyData = new(); + public IReadOnlyDictionary RawBodyData + { + get { return this._rawBodyData.Freeze(); } + } + + /// + /// The API accepts any of the following: + /// + /// - **Binary data** – send the raw bytes as `multipart/form-data`. - **HTTP + /// / HTTPS URL** – a publicly reachable URL that ImageKit’s servers can fetch. + /// - **Base64 string** – the file encoded as a Base64 data URI or plain Base64. + /// + /// When supplying a URL, the server must receive the response headers within + /// 8 seconds; otherwise the request fails with 400 Bad Request. + /// + public required BinaryContent File + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("file"); + } + init { this._rawBodyData.Set("file", value); } + } + + /// + /// The name with which the file has to be uploaded. + /// + public required string FileName + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("fileName"); + } + init { this._rawBodyData.Set("fileName", value); } + } + + /// + /// This is the client-generated JSON Web Token (JWT). The ImageKit.io server + /// uses it to authenticate and check that the upload request parameters have + /// not been tampered with after the token has been generated. Learn how to create + /// the token on the page below. This field is only required for authentication + /// when uploading a file from the client side. + /// + /// **Note**: Sending a JWT that has been used in the past will result in + /// a validation error. Even if your previous request resulted in an error, you + /// should always send a new token. + /// + /// **⚠️Warning**: JWT must be generated on the server-side because it + /// is generated using your account's private API key. This field is required + /// for authentication when uploading a file from the client-side. + /// + public string? Token + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("token"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("token", value); + } + } + + /// + /// Server-side checks to run on the asset. Read more about [Upload API checks](/docs/api-reference/upload-file/upload-file-v2#upload-api-checks). + /// + public string? Checks + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("checks"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("checks", value); + } + } + + /// + /// Define an important area in the image. This is only relevant for image type files. + /// + /// - To be passed as a string with the x and y coordinates of the top-left + /// corner, and width and height of the area of interest in the format `x,y,width,height`. + /// For example - `10,10,100,100` - Can be used with fo-customtransformation. + /// - If this field is not specified and the file is overwritten, then customCoordinates + /// will be removed. + /// + public string? CustomCoordinates + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("customCoordinates"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("customCoordinates", value); + } + } + + /// + /// JSON key-value pairs to associate with the asset. Create the custom metadata + /// fields before setting these values. + /// + public IReadOnlyDictionary? CustomMetadata + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass>( + "customMetadata" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set?>( + "customMetadata", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Optional text to describe the contents of the file. + /// + public string? Description + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("description"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("description", value); + } + } + + /// + /// Array of extensions to be applied to the asset. Each extension can be configured + /// with specific parameters based on the extension type. + /// + public IReadOnlyList? Extensions + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct>("extensions"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set?>( + "extensions", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// The folder path in which the image has to be uploaded. If the folder(s) didn't + /// exist before, a new folder(s) is created. Using multiple `/` creates a nested + /// folder. + /// + public string? Folder + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("folder"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("folder", value); + } + } + + /// + /// Whether to mark the file as private or not. + /// + /// If `true`, the file is marked as private and is accessible only using + /// named transformation or signed URL. + /// + public bool? IsPrivateFile + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct("isPrivateFile"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("isPrivateFile", value); + } + } + + /// + /// Whether to upload file as published or not. + /// + /// If `false`, the file is marked as unpublished, which restricts access + /// to the file only via the media library. Files in draft or unpublished state + /// can only be publicly accessed after being published. + /// + /// The option to upload in draft state is only available in custom enterprise + /// pricing plans. + /// + public bool? IsPublished + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct("isPublished"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("isPublished", value); + } + } + + /// + /// If set to `true` and a file already exists at the exact location, its AITags + /// will be removed. Set `overwriteAITags` to `false` to preserve AITags. + /// + public bool? OverwriteAITags + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct("overwriteAITags"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("overwriteAITags", value); + } + } + + /// + /// If the request does not have `customMetadata`, and a file already exists + /// at the exact location, existing customMetadata will be removed. + /// + public bool? OverwriteCustomMetadata + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct("overwriteCustomMetadata"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("overwriteCustomMetadata", value); + } + } + + /// + /// If `false` and `useUniqueFileName` is also `false`, and a file already exists + /// at the exact location, upload API will return an error immediately. + /// + public bool? OverwriteFile + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct("overwriteFile"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("overwriteFile", value); + } + } + + /// + /// If the request does not have `tags`, and a file already exists at the exact + /// location, existing tags will be removed. + /// + public bool? OverwriteTags + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct("overwriteTags"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("overwriteTags", value); + } + } + + /// + /// Array of response field keys to include in the API response body. + /// + public IReadOnlyList>? ResponseFields + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct< + ImmutableArray> + >("responseFields"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set>?>( + "responseFields", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Set the tags while uploading the file. Provide an array of tag strings (e.g. + /// `["tag1", "tag2", "tag3"]`). The combined length of all tag characters must + /// not exceed 500, and the `%` character is not allowed. If this field is not + /// specified and the file is overwritten, the existing tags will be removed. + /// + public IReadOnlyList? Tags + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct>("tags"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set?>( + "tags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Configure pre-processing (`pre`) and post-processing (`post`) transformations. + /// + /// - `pre` — applied before the file is uploaded to the Media Library. + /// Useful for reducing file size or applying basic optimizations upfront + /// (e.g., resize, compress). + /// + /// - `post` — applied immediately after upload. Ideal for generating + /// transformed versions (like video encodes or thumbnails) in advance, so they're + /// ready for delivery without delay. + /// + /// You can mix and match any combination of post-processing types. + /// + public Transformation? Transformation + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("transformation"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("transformation", value); + } + } + + /// + /// Whether to use a unique filename for this file or not. + /// + /// If `true`, ImageKit.io will add a unique suffix to the filename parameter + /// to get a unique filename. + /// + /// If `false`, then the image is uploaded with the provided filename parameter, + /// and any existing file with the same name is replaced. + /// + public bool? UseUniqueFileName + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct("useUniqueFileName"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("useUniqueFileName", value); + } + } + + /// + /// The final status of extensions after they have completed execution will be + /// delivered to this endpoint as a POST request. [Learn more](/docs/api-reference/digital-asset-management-dam/managing-assets/update-file-details#webhook-payload-structure) + /// about the webhook payload structure. + /// + public string? WebhookUrl + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("webhookUrl"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("webhookUrl", value); + } + } + + public FileUploadParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileUploadParams(FileUploadParams fileUploadParams) + : base(fileUploadParams) + { + this._rawBodyData = new(fileUploadParams._rawBodyData); + } +#pragma warning restore CS8618 + + public FileUploadParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileUploadParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + FrozenDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } +#pragma warning restore CS8618 + + /// + public static FileUploadParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + FrozenDictionary.ToFrozenDictionary(rawBodyData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this._rawBodyData.Freeze()), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(FileUploadParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this._rawBodyData.Equals(other._rawBodyData); + } + + public override System::Uri Url(ClientOptions options) + { + return new System::UriBuilder( + UploadHelpers.GetUploadBaseUrl(options.BaseUrl).TrimEnd('/') + "/api/v2/files/upload" + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() => UploadHelpers.SerializeUploadBody(RawBodyData); + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} + +[JsonConverter(typeof(ResponseFieldConverter))] +public enum ResponseField +{ + Tags, + CustomCoordinates, + IsPrivateFile, + EmbeddedMetadata, + IsPublished, + CustomMetadata, + Metadata, + SelectedFieldsSchema, +} + +sealed class ResponseFieldConverter : JsonConverter +{ + public override ResponseField Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "tags" => ResponseField.Tags, + "customCoordinates" => ResponseField.CustomCoordinates, + "isPrivateFile" => ResponseField.IsPrivateFile, + "embeddedMetadata" => ResponseField.EmbeddedMetadata, + "isPublished" => ResponseField.IsPublished, + "customMetadata" => ResponseField.CustomMetadata, + "metadata" => ResponseField.Metadata, + "selectedFieldsSchema" => ResponseField.SelectedFieldsSchema, + _ => (ResponseField)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + ResponseField value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + ResponseField.Tags => "tags", + ResponseField.CustomCoordinates => "customCoordinates", + ResponseField.IsPrivateFile => "isPrivateFile", + ResponseField.EmbeddedMetadata => "embeddedMetadata", + ResponseField.IsPublished => "isPublished", + ResponseField.CustomMetadata => "customMetadata", + ResponseField.Metadata => "metadata", + ResponseField.SelectedFieldsSchema => "selectedFieldsSchema", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Configure pre-processing (`pre`) and post-processing (`post`) transformations. +/// +/// - `pre` — applied before the file is uploaded to the Media Library. +/// Useful for reducing file size or applying basic optimizations upfront (e.g., +/// resize, compress). +/// +/// - `post` — applied immediately after upload. Ideal for generating transformed +/// versions (like video encodes or thumbnails) in advance, so they're ready for delivery +/// without delay. +/// +/// You can mix and match any combination of post-processing types. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Transformation : JsonModel +{ + /// + /// List of transformations to apply *after* the file is uploaded. Each item + /// must match one of the following types: `transformation`, `gif-to-video`, `thumbnail`, + /// `abs`. + /// + public IReadOnlyList? Post + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("post"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "post", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Transformation string to apply before uploading the file to the Media Library. + /// Useful for optimizing files at ingestion. + /// + public string? Pre + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("pre"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("pre", value); + } + } + + /// + public override void Validate() + { + foreach (var item in this.Post ?? []) + { + item.Validate(); + } + _ = this.Pre; + } + + public Transformation() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Transformation(Transformation transformation) + : base(transformation) { } +#pragma warning restore CS8618 + + public Transformation(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Transformation(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Transformation FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class TransformationFromRaw : IFromRawJson +{ + /// + public Transformation FromRawUnchecked(IReadOnlyDictionary rawData) => + Transformation.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(PostConverter))] +public record class Post : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public JsonElement Type + { + get + { + return Match( + transformation: (x) => x.Type, + gifToVideo: (x) => x.Type, + thumbnail: (x) => x.Type, + abs: (x) => x.Type + ); + } + } + + public string? ValueValue + { + get + { + return Match( + transformation: (x) => x.ValueValue, + gifToVideo: (x) => x.Value, + thumbnail: (x) => x.Value, + abs: (x) => x.Value + ); + } + } + + public Post(PostTransformation value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Post(GifToVideo value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Post(Thumbnail value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Post(Abs value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Post(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickTransformation(out var value)) { + /// // `value` is of type `PostTransformation` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickTransformation([NotNullWhen(true)] out PostTransformation? value) + { + value = this.Value as PostTransformation; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickGifToVideo(out var value)) { + /// // `value` is of type `GifToVideo` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickGifToVideo([NotNullWhen(true)] out GifToVideo? value) + { + value = this.Value as GifToVideo; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickThumbnail(out var value)) { + /// // `value` is of type `Thumbnail` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickThumbnail([NotNullWhen(true)] out Thumbnail? value) + { + value = this.Value as Thumbnail; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickAbs(out var value)) { + /// // `value` is of type `Abs` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickAbs([NotNullWhen(true)] out Abs? value) + { + value = this.Value as Abs; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (PostTransformation value) => {...}, + /// (GifToVideo value) => {...}, + /// (Thumbnail value) => {...}, + /// (Abs value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action transformation, + System::Action gifToVideo, + System::Action thumbnail, + System::Action abs + ) + { + switch (this.Value) + { + case PostTransformation value: + transformation(value); + break; + case GifToVideo value: + gifToVideo(value); + break; + case Thumbnail value: + thumbnail(value); + break; + case Abs value: + abs(value); + break; + default: + throw new ImageKitInvalidDataException("Data did not match any variant of Post"); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (PostTransformation value) => {...}, + /// (GifToVideo value) => {...}, + /// (Thumbnail value) => {...}, + /// (Abs value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func transformation, + System::Func gifToVideo, + System::Func thumbnail, + System::Func abs + ) + { + return this.Value switch + { + PostTransformation value => transformation(value), + GifToVideo value => gifToVideo(value), + Thumbnail value => thumbnail(value), + Abs value => abs(value), + _ => throw new ImageKitInvalidDataException("Data did not match any variant of Post"), + }; + } + + public static implicit operator Post(PostTransformation value) => new(value); + + public static implicit operator Post(GifToVideo value) => new(value); + + public static implicit operator Post(Thumbnail value) => new(value); + + public static implicit operator Post(Abs value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of Post"); + } + this.Switch( + (transformation) => transformation.Validate(), + (gifToVideo) => gifToVideo.Validate(), + (thumbnail) => thumbnail.Validate(), + (abs) => abs.Validate() + ); + } + + public virtual bool Equals(Post? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + PostTransformation _ => 0, + GifToVideo _ => 1, + Thumbnail _ => 2, + Abs _ => 3, + _ => -1, + }; + } +} + +sealed class PostConverter : JsonConverter +{ + public override Post? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + string? type; + try + { + type = element.GetProperty("type").GetString(); + } + catch + { + type = null; + } + + switch (type) + { + case "transformation": + { + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "gif-to-video": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "thumbnail": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "abs": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + default: + { + return new Post(element); + } + } + } + + public override void Write(Utf8JsonWriter writer, Post value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class PostTransformation : JsonModel +{ + /// + /// Transformation type. + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Transformation string (e.g. `w-200,h-200`). Same syntax as ImageKit URL-based + /// transformations. + /// + public required string ValueValue + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("value"); + } + init { this._rawData.Set("value", value); } + } + + /// + public override void Validate() + { + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("transformation"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.ValueValue; + } + + public PostTransformation() + { + this.Type = JsonSerializer.SerializeToElement("transformation"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public PostTransformation(PostTransformation postTransformation) + : base(postTransformation) { } +#pragma warning restore CS8618 + + public PostTransformation(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("transformation"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + PostTransformation(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static PostTransformation FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public PostTransformation(string valueValue) + : this() + { + this.ValueValue = valueValue; + } +} + +class PostTransformationFromRaw : IFromRawJson +{ + /// + public PostTransformation FromRawUnchecked(IReadOnlyDictionary rawData) => + PostTransformation.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class GifToVideo : JsonModel +{ + /// + /// Converts an animated GIF into an MP4. + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Optional transformation string to apply to the output video. **Example**: + /// `q-80` + /// + public string? Value + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("value"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("value", value); + } + } + + /// + public override void Validate() + { + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("gif-to-video"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.Value; + } + + public GifToVideo() + { + this.Type = JsonSerializer.SerializeToElement("gif-to-video"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public GifToVideo(GifToVideo gifToVideo) + : base(gifToVideo) { } +#pragma warning restore CS8618 + + public GifToVideo(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("gif-to-video"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + GifToVideo(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static GifToVideo FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class GifToVideoFromRaw : IFromRawJson +{ + /// + public GifToVideo FromRawUnchecked(IReadOnlyDictionary rawData) => + GifToVideo.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Thumbnail : JsonModel +{ + /// + /// Generates a thumbnail image. + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Optional transformation string. **Example**: `w-150,h-150` + /// + public string? Value + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("value"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("value", value); + } + } + + /// + public override void Validate() + { + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("thumbnail"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.Value; + } + + public Thumbnail() + { + this.Type = JsonSerializer.SerializeToElement("thumbnail"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Thumbnail(Thumbnail thumbnail) + : base(thumbnail) { } +#pragma warning restore CS8618 + + public Thumbnail(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("thumbnail"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Thumbnail(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Thumbnail FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ThumbnailFromRaw : IFromRawJson +{ + /// + public Thumbnail FromRawUnchecked(IReadOnlyDictionary rawData) => + Thumbnail.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Abs : JsonModel +{ + /// + /// Streaming protocol to use (`hls` or `dash`). + /// + public required ApiEnum Protocol + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass>("protocol"); + } + init { this._rawData.Set("protocol", value); } + } + + /// + /// Adaptive Bitrate Streaming (ABS) setup. + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// List of different representations you want to create separated by an underscore. + /// + public required string Value + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("value"); + } + init { this._rawData.Set("value", value); } + } + + /// + public override void Validate() + { + this.Protocol.Validate(); + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("abs"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.Value; + } + + public Abs() + { + this.Type = JsonSerializer.SerializeToElement("abs"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Abs(Abs abs) + : base(abs) { } +#pragma warning restore CS8618 + + public Abs(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("abs"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Abs(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Abs FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class AbsFromRaw : IFromRawJson +{ + /// + public Abs FromRawUnchecked(IReadOnlyDictionary rawData) => + Abs.FromRawUnchecked(rawData); +} + +/// +/// Streaming protocol to use (`hls` or `dash`). +/// +[JsonConverter(typeof(ProtocolConverter))] +public enum Protocol +{ + Hls, + Dash, +} + +sealed class ProtocolConverter : JsonConverter +{ + public override Protocol Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "hls" => Protocol.Hls, + "dash" => Protocol.Dash, + _ => (Protocol)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, Protocol value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + Protocol.Hls => "hls", + Protocol.Dash => "dash", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} diff --git a/src/Imagekit/Models/Beta/V2/Files/FileUploadResponse.cs b/src/Imagekit/Models/Beta/V2/Files/FileUploadResponse.cs new file mode 100644 index 00000000..019faa01 --- /dev/null +++ b/src/Imagekit/Models/Beta/V2/Files/FileUploadResponse.cs @@ -0,0 +1,1034 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using Files = Imagekit.Models.Files; +using System = System; + +namespace Imagekit.Models.Beta.V2.Files; + +/// +/// Object containing details of a successful upload. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class FileUploadResponse : JsonModel +{ + /// + /// An array of tags assigned to the uploaded file by auto tagging. + /// + public IReadOnlyList? AITags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("AITags"); + } + init + { + this._rawData.Set?>( + "AITags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// The audio codec used in the video (only for video). + /// + public string? AudioCodec + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("audioCodec"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("audioCodec", value); + } + } + + /// + /// The bit rate of the video in kbps (only for video). + /// + public long? BitRate + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("bitRate"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("bitRate", value); + } + } + + /// + /// Value of custom coordinates associated with the image in the format `x,y,width,height`. + /// If `customCoordinates` are not defined, then it is `null`. Send `customCoordinates` + /// in `responseFields` in API request to get the value of this field. + /// + public string? CustomCoordinates + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("customCoordinates"); + } + init { this._rawData.Set("customCoordinates", value); } + } + + /// + /// A key-value data associated with the asset. Use `responseField` in API request + /// to get `customMetadata` in the upload API response. Before setting any custom + /// metadata on an asset, you have to create the field using custom metadata fields + /// API. Send `customMetadata` in `responseFields` in API request to get the value + /// of this field. + /// + public IReadOnlyDictionary? CustomMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "customMetadata" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "customMetadata", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Optional text to describe the contents of the file. Can be set by the user + /// or the ai-auto-description extension. + /// + public string? Description + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("description"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("description", value); + } + } + + /// + /// The duration of the video in seconds (only for video). + /// + public long? Duration + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("duration"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("duration", value); + } + } + + /// + /// Consolidated embedded metadata associated with the file. It includes exif, + /// iptc, and xmp data. Send `embeddedMetadata` in `responseFields` in API request + /// to get embeddedMetadata in the upload API response. + /// + public IReadOnlyDictionary? EmbeddedMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "embeddedMetadata" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "embeddedMetadata", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Extension names with their processing status at the time of completion of + /// the request. It could have one of the following status values: + /// + /// `success`: The extension has been successfully applied. `failed`: The + /// extension has failed and will not be retried. `pending`: The extension will + /// finish processing in some time. On completion, the final status (success + /// / failed) will be sent to the `webhookUrl` provided. + /// + /// If no extension was requested, then this parameter is not returned. + /// + public ExtensionStatus? ExtensionStatus + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("extensionStatus"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("extensionStatus", value); + } + } + + /// + /// Unique fileId. Store this fileld in your database, as this will be used to + /// perform update action on this file. + /// + public string? FileID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("fileId"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("fileId", value); + } + } + + /// + /// The relative path of the file in the media library e.g. `/marketing-assets/new-banner.jpg`. + /// + public string? FilePath + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("filePath"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("filePath", value); + } + } + + /// + /// Type of the uploaded file. Possible values are `image`, `non-image`. + /// + public string? FileType + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("fileType"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("fileType", value); + } + } + + /// + /// Height of the image in pixels (Only for images) + /// + public double? Height + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("height"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("height", value); + } + } + + /// + /// Is the file marked as private. It can be either `true` or `false`. Send `isPrivateFile` + /// in `responseFields` in API request to get the value of this field. + /// + public bool? IsPrivateFile + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("isPrivateFile"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("isPrivateFile", value); + } + } + + /// + /// Is the file published or in draft state. It can be either `true` or `false`. + /// Send `isPublished` in `responseFields` in API request to get the value of + /// this field. + /// + public bool? IsPublished + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("isPublished"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("isPublished", value); + } + } + + /// + /// Legacy metadata. Send `metadata` in `responseFields` in API request to get + /// metadata in the upload API response. + /// + public Files::FileMetadata? Metadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("metadata"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("metadata", value); + } + } + + /// + /// Name of the asset. + /// + public string? Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("name"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("name", value); + } + } + + /// + /// This field is included in the response only if the Path policy feature is + /// available in the plan. It contains schema definitions for the custom metadata + /// fields selected for the specified file path. Field selection can only be + /// done when the Path policy feature is enabled. + /// + /// Keys are the names of the custom metadata fields; the value object + /// has details about the custom metadata schema. + /// + public IReadOnlyDictionary? SelectedFieldsSchema + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + FrozenDictionary + >("selectedFieldsSchema"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "selectedFieldsSchema", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Size of the image file in Bytes. + /// + public double? Size + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("size"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("size", value); + } + } + + /// + /// The array of tags associated with the asset. If no tags are set, it will be + /// `null`. Send `tags` in `responseFields` in API request to get the value of + /// this field. + /// + public IReadOnlyList? Tags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("tags"); + } + init + { + this._rawData.Set?>( + "tags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// In the case of an image, a small thumbnail URL. + /// + public string? ThumbnailUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("thumbnailUrl"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("thumbnailUrl", value); + } + } + + /// + /// A publicly accessible URL of the file. + /// + public string? Url + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("url"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("url", value); + } + } + + /// + /// An object containing the file or file version's `id` (versionId) and `name`. + /// + public VersionInfo? VersionInfo + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("versionInfo"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("versionInfo", value); + } + } + + /// + /// The video codec used in the video (only for video). + /// + public string? VideoCodec + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("videoCodec"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("videoCodec", value); + } + } + + /// + /// Width of the image in pixels (Only for Images) + /// + public double? Width + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("width"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("width", value); + } + } + + /// + public override void Validate() + { + foreach (var item in this.AITags ?? []) + { + item.Validate(); + } + _ = this.AudioCodec; + _ = this.BitRate; + _ = this.CustomCoordinates; + _ = this.CustomMetadata; + _ = this.Description; + _ = this.Duration; + _ = this.EmbeddedMetadata; + this.ExtensionStatus?.Validate(); + _ = this.FileID; + _ = this.FilePath; + _ = this.FileType; + _ = this.Height; + _ = this.IsPrivateFile; + _ = this.IsPublished; + this.Metadata?.Validate(); + _ = this.Name; + if (this.SelectedFieldsSchema != null) + { + foreach (var item in this.SelectedFieldsSchema.Values) + { + item.Validate(); + } + } + _ = this.Size; + _ = this.Tags; + _ = this.ThumbnailUrl; + _ = this.Url; + this.VersionInfo?.Validate(); + _ = this.VideoCodec; + _ = this.Width; + } + + public FileUploadResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileUploadResponse(FileUploadResponse fileUploadResponse) + : base(fileUploadResponse) { } +#pragma warning restore CS8618 + + public FileUploadResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileUploadResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FileUploadResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FileUploadResponseFromRaw : IFromRawJson +{ + /// + public FileUploadResponse FromRawUnchecked(IReadOnlyDictionary rawData) => + FileUploadResponse.FromRawUnchecked(rawData); +} + +/// +/// Extension names with their processing status at the time of completion of the +/// request. It could have one of the following status values: +/// +/// `success`: The extension has been successfully applied. `failed`: The extension +/// has failed and will not be retried. `pending`: The extension will finish processing +/// in some time. On completion, the final status (success / failed) will be sent +/// to the `webhookUrl` provided. +/// +/// If no extension was requested, then this parameter is not returned. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class ExtensionStatus : JsonModel +{ + public ApiEnum? AIAutoDescription + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "ai-auto-description" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ai-auto-description", value); + } + } + + public ApiEnum? AITasks + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("ai-tasks"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ai-tasks", value); + } + } + + public ApiEnum? AwsAutoTagging + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "aws-auto-tagging" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("aws-auto-tagging", value); + } + } + + public ApiEnum? GoogleAutoTagging + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "google-auto-tagging" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("google-auto-tagging", value); + } + } + + public ApiEnum? RemoveBg + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("remove-bg"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("remove-bg", value); + } + } + + /// + public override void Validate() + { + this.AIAutoDescription?.Validate(); + this.AITasks?.Validate(); + this.AwsAutoTagging?.Validate(); + this.GoogleAutoTagging?.Validate(); + this.RemoveBg?.Validate(); + } + + public ExtensionStatus() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExtensionStatus(ExtensionStatus extensionStatus) + : base(extensionStatus) { } +#pragma warning restore CS8618 + + public ExtensionStatus(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExtensionStatus(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExtensionStatus FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ExtensionStatusFromRaw : IFromRawJson +{ + /// + public ExtensionStatus FromRawUnchecked(IReadOnlyDictionary rawData) => + ExtensionStatus.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(AIAutoDescriptionConverter))] +public enum AIAutoDescription +{ + Success, + Pending, + Failed, +} + +sealed class AIAutoDescriptionConverter : JsonConverter +{ + public override AIAutoDescription Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "success" => AIAutoDescription.Success, + "pending" => AIAutoDescription.Pending, + "failed" => AIAutoDescription.Failed, + _ => (AIAutoDescription)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + AIAutoDescription value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + AIAutoDescription.Success => "success", + AIAutoDescription.Pending => "pending", + AIAutoDescription.Failed => "failed", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +[JsonConverter(typeof(AITasksConverter))] +public enum AITasks +{ + Success, + Pending, + Failed, +} + +sealed class AITasksConverter : JsonConverter +{ + public override AITasks Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "success" => AITasks.Success, + "pending" => AITasks.Pending, + "failed" => AITasks.Failed, + _ => (AITasks)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, AITasks value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + AITasks.Success => "success", + AITasks.Pending => "pending", + AITasks.Failed => "failed", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +[JsonConverter(typeof(AwsAutoTaggingConverter))] +public enum AwsAutoTagging +{ + Success, + Pending, + Failed, +} + +sealed class AwsAutoTaggingConverter : JsonConverter +{ + public override AwsAutoTagging Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "success" => AwsAutoTagging.Success, + "pending" => AwsAutoTagging.Pending, + "failed" => AwsAutoTagging.Failed, + _ => (AwsAutoTagging)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + AwsAutoTagging value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + AwsAutoTagging.Success => "success", + AwsAutoTagging.Pending => "pending", + AwsAutoTagging.Failed => "failed", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +[JsonConverter(typeof(GoogleAutoTaggingConverter))] +public enum GoogleAutoTagging +{ + Success, + Pending, + Failed, +} + +sealed class GoogleAutoTaggingConverter : JsonConverter +{ + public override GoogleAutoTagging Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "success" => GoogleAutoTagging.Success, + "pending" => GoogleAutoTagging.Pending, + "failed" => GoogleAutoTagging.Failed, + _ => (GoogleAutoTagging)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + GoogleAutoTagging value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + GoogleAutoTagging.Success => "success", + GoogleAutoTagging.Pending => "pending", + GoogleAutoTagging.Failed => "failed", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +[JsonConverter(typeof(RemoveBgConverter))] +public enum RemoveBg +{ + Success, + Pending, + Failed, +} + +sealed class RemoveBgConverter : JsonConverter +{ + public override RemoveBg Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "success" => RemoveBg.Success, + "pending" => RemoveBg.Pending, + "failed" => RemoveBg.Failed, + _ => (RemoveBg)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, RemoveBg value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + RemoveBg.Success => "success", + RemoveBg.Pending => "pending", + RemoveBg.Failed => "failed", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} diff --git a/src/Imagekit/Models/Cache/Invalidation/InvalidationCreateParams.cs b/src/Imagekit/Models/Cache/Invalidation/InvalidationCreateParams.cs new file mode 100644 index 00000000..3b841086 --- /dev/null +++ b/src/Imagekit/Models/Cache/Invalidation/InvalidationCreateParams.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; +using Text = System.Text; + +namespace Imagekit.Models.Cache.Invalidation; + +/// +/// This API will purge CDN cache and ImageKit.io's internal cache for a file. Note: +/// Purge cache is an asynchronous process and it may take some time to reflect the changes. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class InvalidationCreateParams : ParamsBase +{ + readonly JsonDictionary _rawBodyData = new(); + public IReadOnlyDictionary RawBodyData + { + get { return this._rawBodyData.Freeze(); } + } + + /// + /// The full URL of the file to be purged. + /// + public required string UrlValue + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("url"); + } + init { this._rawBodyData.Set("url", value); } + } + + public InvalidationCreateParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public InvalidationCreateParams(InvalidationCreateParams invalidationCreateParams) + : base(invalidationCreateParams) + { + this._rawBodyData = new(invalidationCreateParams._rawBodyData); + } +#pragma warning restore CS8618 + + public InvalidationCreateParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + InvalidationCreateParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + FrozenDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } +#pragma warning restore CS8618 + + /// + public static InvalidationCreateParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + FrozenDictionary.ToFrozenDictionary(rawBodyData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this._rawBodyData.Freeze()), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(InvalidationCreateParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this._rawBodyData.Equals(other._rawBodyData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder(options.BaseUrl.ToString().TrimEnd('/') + "/v1/files/purge") + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Cache/Invalidation/InvalidationCreateResponse.cs b/src/Imagekit/Models/Cache/Invalidation/InvalidationCreateResponse.cs new file mode 100644 index 00000000..dece3abb --- /dev/null +++ b/src/Imagekit/Models/Cache/Invalidation/InvalidationCreateResponse.cs @@ -0,0 +1,79 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models.Cache.Invalidation; + +[JsonConverter( + typeof(JsonModelConverter) +)] +public sealed record class InvalidationCreateResponse : JsonModel +{ + /// + /// Unique identifier of the purge request. This can be used to check the status + /// of the purge request. + /// + public string? RequestID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("requestId"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("requestId", value); + } + } + + /// + public override void Validate() + { + _ = this.RequestID; + } + + public InvalidationCreateResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public InvalidationCreateResponse(InvalidationCreateResponse invalidationCreateResponse) + : base(invalidationCreateResponse) { } +#pragma warning restore CS8618 + + public InvalidationCreateResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + InvalidationCreateResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static InvalidationCreateResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class InvalidationCreateResponseFromRaw : IFromRawJson +{ + /// + public InvalidationCreateResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) => InvalidationCreateResponse.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Cache/Invalidation/InvalidationGetParams.cs b/src/Imagekit/Models/Cache/Invalidation/InvalidationGetParams.cs new file mode 100644 index 00000000..1db6c24f --- /dev/null +++ b/src/Imagekit/Models/Cache/Invalidation/InvalidationGetParams.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Models.Cache.Invalidation; + +/// +/// This API returns the status of a purge cache request. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class InvalidationGetParams : ParamsBase +{ + public string? RequestID { get; init; } + + public InvalidationGetParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public InvalidationGetParams(InvalidationGetParams invalidationGetParams) + : base(invalidationGetParams) + { + this.RequestID = invalidationGetParams.RequestID; + } +#pragma warning restore CS8618 + + public InvalidationGetParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + InvalidationGetParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + string requestID + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this.RequestID = requestID; + } +#pragma warning restore CS8618 + + /// + public static InvalidationGetParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + string requestID + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + requestID + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["RequestID"] = JsonSerializer.SerializeToElement(this.RequestID), + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(InvalidationGetParams? other) + { + if (other == null) + { + return false; + } + return (this.RequestID?.Equals(other.RequestID) ?? other.RequestID == null) + && this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + + string.Format("/v1/files/purge/{0}", this.RequestID) + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Cache/Invalidation/InvalidationGetResponse.cs b/src/Imagekit/Models/Cache/Invalidation/InvalidationGetResponse.cs new file mode 100644 index 00000000..4ba504d5 --- /dev/null +++ b/src/Imagekit/Models/Cache/Invalidation/InvalidationGetResponse.cs @@ -0,0 +1,121 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models.Cache.Invalidation; + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class InvalidationGetResponse : JsonModel +{ + /// + /// Status of the purge request. + /// + public ApiEnum? Status + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("status"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("status", value); + } + } + + /// + public override void Validate() + { + this.Status?.Validate(); + } + + public InvalidationGetResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public InvalidationGetResponse(InvalidationGetResponse invalidationGetResponse) + : base(invalidationGetResponse) { } +#pragma warning restore CS8618 + + public InvalidationGetResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + InvalidationGetResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static InvalidationGetResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class InvalidationGetResponseFromRaw : IFromRawJson +{ + /// + public InvalidationGetResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) => InvalidationGetResponse.FromRawUnchecked(rawData); +} + +/// +/// Status of the purge request. +/// +[JsonConverter(typeof(StatusConverter))] +public enum Status +{ + Pending, + Completed, +} + +sealed class StatusConverter : JsonConverter +{ + public override Status Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "Pending" => Status.Pending, + "Completed" => Status.Completed, + _ => (Status)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, Status value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + Status.Pending => "Pending", + Status.Completed => "Completed", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} diff --git a/src/Imagekit/Models/CustomMetadataFields/CustomMetadataField.cs b/src/Imagekit/Models/CustomMetadataFields/CustomMetadataField.cs new file mode 100644 index 00000000..5ac68722 --- /dev/null +++ b/src/Imagekit/Models/CustomMetadataFields/CustomMetadataField.cs @@ -0,0 +1,1909 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models.CustomMetadataFields; + +/// +/// Object containing details of a custom metadata field. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class CustomMetadataField : JsonModel +{ + /// + /// Unique identifier for the custom metadata field. Use this to update the field. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + /// + /// Human readable name of the custom metadata field. This name is displayed + /// as form field label to the users while setting field value on the asset in + /// the media library UI. + /// + public required string Label + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("label"); + } + init { this._rawData.Set("label", value); } + } + + /// + /// API name of the custom metadata field. This becomes the key while setting + /// `customMetadata` (key-value object) for an asset using upload or update API. + /// + public required string Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("name"); + } + init { this._rawData.Set("name", value); } + } + + /// + /// An object that describes the rules for the custom metadata field value. + /// + public required CustomMetadataFieldSchema Schema + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("schema"); + } + init { this._rawData.Set("schema", value); } + } + + /// + public override void Validate() + { + _ = this.ID; + _ = this.Label; + _ = this.Name; + this.Schema.Validate(); + } + + public CustomMetadataField() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public CustomMetadataField(CustomMetadataField customMetadataField) + : base(customMetadataField) { } +#pragma warning restore CS8618 + + public CustomMetadataField(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + CustomMetadataField(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static CustomMetadataField FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class CustomMetadataFieldFromRaw : IFromRawJson +{ + /// + public CustomMetadataField FromRawUnchecked(IReadOnlyDictionary rawData) => + CustomMetadataField.FromRawUnchecked(rawData); +} + +/// +/// An object that describes the rules for the custom metadata field value. +/// +[JsonConverter( + typeof(JsonModelConverter) +)] +public sealed record class CustomMetadataFieldSchema : JsonModel +{ + /// + /// Type of the custom metadata field. + /// + public required ApiEnum Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass>( + "type" + ); + } + init { this._rawData.Set("type", value); } + } + + /// + /// The default value for this custom metadata field. Data type of default value + /// depends on the field type. + /// + public CustomMetadataFieldSchemaDefaultValue? DefaultValue + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass( + "defaultValue" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("defaultValue", value); + } + } + + /// + /// Specifies if the this custom metadata field is required or not. + /// + public bool? IsValueRequired + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("isValueRequired"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("isValueRequired", value); + } + } + + /// + /// Maximum length of string. Only set if `type` is set to `Text` or `Textarea`. + /// + public double? MaxLength + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("maxLength"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("maxLength", value); + } + } + + /// + /// Maximum value of the field. Only set if field type is `Date` or `Number`. + /// For `Date` type field, the value will be in ISO8601 string format. For `Number` + /// type field, it will be a numeric value. + /// + public CustomMetadataFieldSchemaMaxValue? MaxValue + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("maxValue"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("maxValue", value); + } + } + + /// + /// Minimum length of string. Only set if `type` is set to `Text` or `Textarea`. + /// + public double? MinLength + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("minLength"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("minLength", value); + } + } + + /// + /// Minimum value of the field. Only set if field type is `Date` or `Number`. + /// For `Date` type field, the value will be in ISO8601 string format. For `Number` + /// type field, it will be a numeric value. + /// + public CustomMetadataFieldSchemaMinValue? MinValue + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("minValue"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("minValue", value); + } + } + + /// + /// An array of allowed values when field type is `SingleSelect` or `MultiSelect`. + /// + public IReadOnlyList? SelectOptions + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct< + ImmutableArray + >("selectOptions"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "selectOptions", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + this.Type.Validate(); + this.DefaultValue?.Validate(); + _ = this.IsValueRequired; + _ = this.MaxLength; + this.MaxValue?.Validate(); + _ = this.MinLength; + this.MinValue?.Validate(); + foreach (var item in this.SelectOptions ?? []) + { + item.Validate(); + } + } + + public CustomMetadataFieldSchema() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public CustomMetadataFieldSchema(CustomMetadataFieldSchema customMetadataFieldSchema) + : base(customMetadataFieldSchema) { } +#pragma warning restore CS8618 + + public CustomMetadataFieldSchema(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + CustomMetadataFieldSchema(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static CustomMetadataFieldSchema FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public CustomMetadataFieldSchema(ApiEnum type) + : this() + { + this.Type = type; + } +} + +class CustomMetadataFieldSchemaFromRaw : IFromRawJson +{ + /// + public CustomMetadataFieldSchema FromRawUnchecked( + IReadOnlyDictionary rawData + ) => CustomMetadataFieldSchema.FromRawUnchecked(rawData); +} + +/// +/// Type of the custom metadata field. +/// +[JsonConverter(typeof(CustomMetadataFieldSchemaTypeConverter))] +public enum CustomMetadataFieldSchemaType +{ + Text, + Textarea, + Number, + Date, + Boolean, + SingleSelect, + MultiSelect, +} + +sealed class CustomMetadataFieldSchemaTypeConverter : JsonConverter +{ + public override CustomMetadataFieldSchemaType Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "Text" => CustomMetadataFieldSchemaType.Text, + "Textarea" => CustomMetadataFieldSchemaType.Textarea, + "Number" => CustomMetadataFieldSchemaType.Number, + "Date" => CustomMetadataFieldSchemaType.Date, + "Boolean" => CustomMetadataFieldSchemaType.Boolean, + "SingleSelect" => CustomMetadataFieldSchemaType.SingleSelect, + "MultiSelect" => CustomMetadataFieldSchemaType.MultiSelect, + _ => (CustomMetadataFieldSchemaType)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + CustomMetadataFieldSchemaType value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + CustomMetadataFieldSchemaType.Text => "Text", + CustomMetadataFieldSchemaType.Textarea => "Textarea", + CustomMetadataFieldSchemaType.Number => "Number", + CustomMetadataFieldSchemaType.Date => "Date", + CustomMetadataFieldSchemaType.Boolean => "Boolean", + CustomMetadataFieldSchemaType.SingleSelect => "SingleSelect", + CustomMetadataFieldSchemaType.MultiSelect => "MultiSelect", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// The default value for this custom metadata field. Data type of default value depends +/// on the field type. +/// +[JsonConverter(typeof(CustomMetadataFieldSchemaDefaultValueConverter))] +public record class CustomMetadataFieldSchemaDefaultValue : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public CustomMetadataFieldSchemaDefaultValue(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldSchemaDefaultValue(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldSchemaDefaultValue(bool value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldSchemaDefaultValue( + IReadOnlyList value, + JsonElement? element = null + ) + { + this.Value = ImmutableArray.ToImmutableArray(value); + this._element = element; + } + + public CustomMetadataFieldSchemaDefaultValue(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type where T is a CustomMetadataFieldSchemaDefaultValueDefaultValueItem. + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickMixed(out var value)) { + /// // `value` is of type `IReadOnlyList<CustomMetadataFieldSchemaDefaultValueDefaultValueItem>` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickMixed( + [NotNullWhen(true)] + out IReadOnlyList? value + ) + { + value = this.Value as IReadOnlyList; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...}, + /// (IReadOnlyList<CustomMetadataFieldSchemaDefaultValueDefaultValueItem> value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool, + System::Action> mixed + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + case IReadOnlyList value: + mixed(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldSchemaDefaultValue" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...}, + /// (IReadOnlyList<CustomMetadataFieldSchemaDefaultValueDefaultValueItem> value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool, + System::Func, T> mixed + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + IReadOnlyList value => mixed( + value + ), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldSchemaDefaultValue" + ), + }; + } + + public static implicit operator CustomMetadataFieldSchemaDefaultValue(string value) => + new(value); + + public static implicit operator CustomMetadataFieldSchemaDefaultValue(double value) => + new(value); + + public static implicit operator CustomMetadataFieldSchemaDefaultValue(bool value) => new(value); + + public static implicit operator CustomMetadataFieldSchemaDefaultValue( + List value + ) => new((IReadOnlyList)value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldSchemaDefaultValue" + ); + } + this.Switch( + (_) => { }, + (_) => { }, + (_) => { }, + (mixed) => + { + foreach (var item in mixed) + { + item.Validate(); + } + } + ); + } + + public virtual bool Equals(CustomMetadataFieldSchemaDefaultValue? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + IReadOnlyList _ => 3, + _ => -1, + }; + } +} + +sealed class CustomMetadataFieldSchemaDefaultValueConverter + : JsonConverter +{ + public override CustomMetadataFieldSchemaDefaultValue? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize< + List + >(element, options); + if (deserialized != null) + { + foreach (var item in deserialized) + { + item.Validate(); + } + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + CustomMetadataFieldSchemaDefaultValue value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(CustomMetadataFieldSchemaDefaultValueDefaultValueItemConverter))] +public record class CustomMetadataFieldSchemaDefaultValueDefaultValueItem : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public CustomMetadataFieldSchemaDefaultValueDefaultValueItem( + string value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldSchemaDefaultValueDefaultValueItem( + double value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldSchemaDefaultValueDefaultValueItem( + bool value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldSchemaDefaultValueDefaultValueItem(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldSchemaDefaultValueDefaultValueItem" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldSchemaDefaultValueDefaultValueItem" + ), + }; + } + + public static implicit operator CustomMetadataFieldSchemaDefaultValueDefaultValueItem( + string value + ) => new(value); + + public static implicit operator CustomMetadataFieldSchemaDefaultValueDefaultValueItem( + double value + ) => new(value); + + public static implicit operator CustomMetadataFieldSchemaDefaultValueDefaultValueItem( + bool value + ) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldSchemaDefaultValueDefaultValueItem" + ); + } + } + + public virtual bool Equals(CustomMetadataFieldSchemaDefaultValueDefaultValueItem? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + _ => -1, + }; + } +} + +sealed class CustomMetadataFieldSchemaDefaultValueDefaultValueItemConverter + : JsonConverter +{ + public override CustomMetadataFieldSchemaDefaultValueDefaultValueItem? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + CustomMetadataFieldSchemaDefaultValueDefaultValueItem value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Maximum value of the field. Only set if field type is `Date` or `Number`. For +/// `Date` type field, the value will be in ISO8601 string format. For `Number` type +/// field, it will be a numeric value. +/// +[JsonConverter(typeof(CustomMetadataFieldSchemaMaxValueConverter))] +public record class CustomMetadataFieldSchemaMaxValue : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public CustomMetadataFieldSchemaMaxValue(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldSchemaMaxValue(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldSchemaMaxValue(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @string, System::Action @double) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldSchemaMaxValue" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @string, System::Func @double) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldSchemaMaxValue" + ), + }; + } + + public static implicit operator CustomMetadataFieldSchemaMaxValue(string value) => new(value); + + public static implicit operator CustomMetadataFieldSchemaMaxValue(double value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldSchemaMaxValue" + ); + } + } + + public virtual bool Equals(CustomMetadataFieldSchemaMaxValue? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + _ => -1, + }; + } +} + +sealed class CustomMetadataFieldSchemaMaxValueConverter + : JsonConverter +{ + public override CustomMetadataFieldSchemaMaxValue? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + CustomMetadataFieldSchemaMaxValue value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Minimum value of the field. Only set if field type is `Date` or `Number`. For +/// `Date` type field, the value will be in ISO8601 string format. For `Number` type +/// field, it will be a numeric value. +/// +[JsonConverter(typeof(CustomMetadataFieldSchemaMinValueConverter))] +public record class CustomMetadataFieldSchemaMinValue : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public CustomMetadataFieldSchemaMinValue(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldSchemaMinValue(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldSchemaMinValue(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @string, System::Action @double) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldSchemaMinValue" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @string, System::Func @double) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldSchemaMinValue" + ), + }; + } + + public static implicit operator CustomMetadataFieldSchemaMinValue(string value) => new(value); + + public static implicit operator CustomMetadataFieldSchemaMinValue(double value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldSchemaMinValue" + ); + } + } + + public virtual bool Equals(CustomMetadataFieldSchemaMinValue? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + _ => -1, + }; + } +} + +sealed class CustomMetadataFieldSchemaMinValueConverter + : JsonConverter +{ + public override CustomMetadataFieldSchemaMinValue? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + CustomMetadataFieldSchemaMinValue value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(CustomMetadataFieldSchemaSelectOptionConverter))] +public record class CustomMetadataFieldSchemaSelectOption : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public CustomMetadataFieldSchemaSelectOption(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldSchemaSelectOption(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldSchemaSelectOption(bool value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldSchemaSelectOption(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldSchemaSelectOption" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldSchemaSelectOption" + ), + }; + } + + public static implicit operator CustomMetadataFieldSchemaSelectOption(string value) => + new(value); + + public static implicit operator CustomMetadataFieldSchemaSelectOption(double value) => + new(value); + + public static implicit operator CustomMetadataFieldSchemaSelectOption(bool value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldSchemaSelectOption" + ); + } + } + + public virtual bool Equals(CustomMetadataFieldSchemaSelectOption? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + _ => -1, + }; + } +} + +sealed class CustomMetadataFieldSchemaSelectOptionConverter + : JsonConverter +{ + public override CustomMetadataFieldSchemaSelectOption? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + CustomMetadataFieldSchemaSelectOption value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} diff --git a/src/Imagekit/Models/CustomMetadataFields/CustomMetadataFieldCreateParams.cs b/src/Imagekit/Models/CustomMetadataFields/CustomMetadataFieldCreateParams.cs new file mode 100644 index 00000000..018ffa18 --- /dev/null +++ b/src/Imagekit/Models/CustomMetadataFields/CustomMetadataFieldCreateParams.cs @@ -0,0 +1,1923 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; +using Text = System.Text; + +namespace Imagekit.Models.CustomMetadataFields; + +/// +/// This API creates a new custom metadata field. Once a custom metadata field is +/// created either through this API or using the dashboard UI, its value can be set +/// on the assets. The value of a field for an asset can be set using the media library +/// UI or programmatically through upload or update assets API. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class CustomMetadataFieldCreateParams : ParamsBase +{ + readonly JsonDictionary _rawBodyData = new(); + public IReadOnlyDictionary RawBodyData + { + get { return this._rawBodyData.Freeze(); } + } + + /// + /// Human readable name of the custom metadata field. This should be unique across + /// all non deleted custom metadata fields. This name is displayed as form field + /// label to the users while setting field value on an asset in the media library UI. + /// + public required string Label + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("label"); + } + init { this._rawBodyData.Set("label", value); } + } + + /// + /// API name of the custom metadata field. This should be unique across all (including + /// deleted) custom metadata fields. + /// + public required string Name + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("name"); + } + init { this._rawBodyData.Set("name", value); } + } + + public required Schema Schema + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("schema"); + } + init { this._rawBodyData.Set("schema", value); } + } + + public CustomMetadataFieldCreateParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public CustomMetadataFieldCreateParams( + CustomMetadataFieldCreateParams customMetadataFieldCreateParams + ) + : base(customMetadataFieldCreateParams) + { + this._rawBodyData = new(customMetadataFieldCreateParams._rawBodyData); + } +#pragma warning restore CS8618 + + public CustomMetadataFieldCreateParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + CustomMetadataFieldCreateParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + FrozenDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } +#pragma warning restore CS8618 + + /// + public static CustomMetadataFieldCreateParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + FrozenDictionary.ToFrozenDictionary(rawBodyData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this._rawBodyData.Freeze()), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(CustomMetadataFieldCreateParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this._rawBodyData.Equals(other._rawBodyData); + } + + public override System::Uri Url(ClientOptions options) + { + return new System::UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + "/v1/customMetadataFields" + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Schema : JsonModel +{ + /// + /// Type of the custom metadata field. + /// + public required ApiEnum Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass< + ApiEnum + >("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// The default value for this custom metadata field. This property is only required + /// if `isValueRequired` property is set to `true`. The value should match the + /// `type` of custom metadata field. + /// + public DefaultValue? DefaultValue + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("defaultValue"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("defaultValue", value); + } + } + + /// + /// Sets this custom metadata field as required. Setting custom metadata fields + /// on an asset will throw error if the value for all required fields are not + /// present in upload or update asset API request body. + /// + public bool? IsValueRequired + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("isValueRequired"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("isValueRequired", value); + } + } + + /// + /// Maximum length of string. Only set this property if `type` is set to `Text` + /// or `Textarea`. + /// + public double? MaxLength + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("maxLength"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("maxLength", value); + } + } + + /// + /// Maximum value of the field. Only set this property if field type is `Date` + /// or `Number`. For `Date` type field, set the minimum date in ISO8601 string + /// format. For `Number` type field, set the minimum numeric value. + /// + public MaxValue? MaxValue + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("maxValue"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("maxValue", value); + } + } + + /// + /// Minimum length of string. Only set this property if `type` is set to `Text` + /// or `Textarea`. + /// + public double? MinLength + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("minLength"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("minLength", value); + } + } + + /// + /// Minimum value of the field. Only set this property if field type is `Date` + /// or `Number`. For `Date` type field, set the minimum date in ISO8601 string + /// format. For `Number` type field, set the minimum numeric value. + /// + public MinValue? MinValue + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("minValue"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("minValue", value); + } + } + + /// + /// An array of allowed values. This property is only required if `type` property + /// is set to `SingleSelect` or `MultiSelect`. + /// + public IReadOnlyList? SelectOptions + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("selectOptions"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "selectOptions", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + this.Type.Validate(); + this.DefaultValue?.Validate(); + _ = this.IsValueRequired; + _ = this.MaxLength; + this.MaxValue?.Validate(); + _ = this.MinLength; + this.MinValue?.Validate(); + foreach (var item in this.SelectOptions ?? []) + { + item.Validate(); + } + } + + public Schema() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Schema(Schema schema) + : base(schema) { } +#pragma warning restore CS8618 + + public Schema(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Schema(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Schema FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public Schema(ApiEnum type) + : this() + { + this.Type = type; + } +} + +class SchemaFromRaw : IFromRawJson +{ + /// + public Schema FromRawUnchecked(IReadOnlyDictionary rawData) => + Schema.FromRawUnchecked(rawData); +} + +/// +/// Type of the custom metadata field. +/// +[JsonConverter(typeof(TypeConverter))] +public enum Type +{ + Text, + Textarea, + Number, + Date, + Boolean, + SingleSelect, + MultiSelect, +} + +sealed class TypeConverter : JsonConverter +{ + public override global::Imagekit.Models.CustomMetadataFields.Type Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "Text" => global::Imagekit.Models.CustomMetadataFields.Type.Text, + "Textarea" => global::Imagekit.Models.CustomMetadataFields.Type.Textarea, + "Number" => global::Imagekit.Models.CustomMetadataFields.Type.Number, + "Date" => global::Imagekit.Models.CustomMetadataFields.Type.Date, + "Boolean" => global::Imagekit.Models.CustomMetadataFields.Type.Boolean, + "SingleSelect" => global::Imagekit.Models.CustomMetadataFields.Type.SingleSelect, + "MultiSelect" => global::Imagekit.Models.CustomMetadataFields.Type.MultiSelect, + _ => (global::Imagekit.Models.CustomMetadataFields.Type)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + global::Imagekit.Models.CustomMetadataFields.Type value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + global::Imagekit.Models.CustomMetadataFields.Type.Text => "Text", + global::Imagekit.Models.CustomMetadataFields.Type.Textarea => "Textarea", + global::Imagekit.Models.CustomMetadataFields.Type.Number => "Number", + global::Imagekit.Models.CustomMetadataFields.Type.Date => "Date", + global::Imagekit.Models.CustomMetadataFields.Type.Boolean => "Boolean", + global::Imagekit.Models.CustomMetadataFields.Type.SingleSelect => "SingleSelect", + global::Imagekit.Models.CustomMetadataFields.Type.MultiSelect => "MultiSelect", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// The default value for this custom metadata field. This property is only required +/// if `isValueRequired` property is set to `true`. The value should match the `type` +/// of custom metadata field. +/// +[JsonConverter(typeof(DefaultValueConverter))] +public record class DefaultValue : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public DefaultValue(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public DefaultValue(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public DefaultValue(bool value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public DefaultValue(IReadOnlyList value, JsonElement? element = null) + { + this.Value = ImmutableArray.ToImmutableArray(value); + this._element = element; + } + + public DefaultValue(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type where T is a DefaultValueItem. + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickMixed(out var value)) { + /// // `value` is of type `IReadOnlyList<DefaultValueItem>` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickMixed([NotNullWhen(true)] out IReadOnlyList? value) + { + value = this.Value as IReadOnlyList; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...}, + /// (IReadOnlyList<DefaultValueItem> value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool, + System::Action> mixed + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + case IReadOnlyList value: + mixed(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of DefaultValue" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...}, + /// (IReadOnlyList<DefaultValueItem> value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool, + System::Func, T> mixed + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + IReadOnlyList value => mixed(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of DefaultValue" + ), + }; + } + + public static implicit operator DefaultValue(string value) => new(value); + + public static implicit operator DefaultValue(double value) => new(value); + + public static implicit operator DefaultValue(bool value) => new(value); + + public static implicit operator DefaultValue(List value) => + new((IReadOnlyList)value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of DefaultValue" + ); + } + this.Switch( + (_) => { }, + (_) => { }, + (_) => { }, + (mixed) => + { + foreach (var item in mixed) + { + item.Validate(); + } + } + ); + } + + public virtual bool Equals(DefaultValue? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + IReadOnlyList _ => 3, + _ => -1, + }; + } +} + +sealed class DefaultValueConverter : JsonConverter +{ + public override DefaultValue? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize>(element, options); + if (deserialized != null) + { + foreach (var item in deserialized) + { + item.Validate(); + } + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + DefaultValue value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(DefaultValueItemConverter))] +public record class DefaultValueItem : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public DefaultValueItem(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public DefaultValueItem(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public DefaultValueItem(bool value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public DefaultValueItem(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of DefaultValueItem" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of DefaultValueItem" + ), + }; + } + + public static implicit operator DefaultValueItem(string value) => new(value); + + public static implicit operator DefaultValueItem(double value) => new(value); + + public static implicit operator DefaultValueItem(bool value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of DefaultValueItem" + ); + } + } + + public virtual bool Equals(DefaultValueItem? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + _ => -1, + }; + } +} + +sealed class DefaultValueItemConverter : JsonConverter +{ + public override DefaultValueItem? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + DefaultValueItem value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Maximum value of the field. Only set this property if field type is `Date` or +/// `Number`. For `Date` type field, set the minimum date in ISO8601 string format. +/// For `Number` type field, set the minimum numeric value. +/// +[JsonConverter(typeof(MaxValueConverter))] +public record class MaxValue : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public MaxValue(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public MaxValue(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public MaxValue(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @string, System::Action @double) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of MaxValue" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @string, System::Func @double) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of MaxValue" + ), + }; + } + + public static implicit operator MaxValue(string value) => new(value); + + public static implicit operator MaxValue(double value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of MaxValue"); + } + } + + public virtual bool Equals(MaxValue? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + _ => -1, + }; + } +} + +sealed class MaxValueConverter : JsonConverter +{ + public override MaxValue? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, MaxValue value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Minimum value of the field. Only set this property if field type is `Date` or +/// `Number`. For `Date` type field, set the minimum date in ISO8601 string format. +/// For `Number` type field, set the minimum numeric value. +/// +[JsonConverter(typeof(MinValueConverter))] +public record class MinValue : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public MinValue(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public MinValue(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public MinValue(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @string, System::Action @double) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of MinValue" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @string, System::Func @double) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of MinValue" + ), + }; + } + + public static implicit operator MinValue(string value) => new(value); + + public static implicit operator MinValue(double value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of MinValue"); + } + } + + public virtual bool Equals(MinValue? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + _ => -1, + }; + } +} + +sealed class MinValueConverter : JsonConverter +{ + public override MinValue? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, MinValue value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(SelectOptionConverter))] +public record class SelectOption : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public SelectOption(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public SelectOption(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public SelectOption(bool value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public SelectOption(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of SelectOption" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of SelectOption" + ), + }; + } + + public static implicit operator SelectOption(string value) => new(value); + + public static implicit operator SelectOption(double value) => new(value); + + public static implicit operator SelectOption(bool value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of SelectOption" + ); + } + } + + public virtual bool Equals(SelectOption? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + _ => -1, + }; + } +} + +sealed class SelectOptionConverter : JsonConverter +{ + public override SelectOption? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + SelectOption value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} diff --git a/src/Imagekit/Models/CustomMetadataFields/CustomMetadataFieldDeleteParams.cs b/src/Imagekit/Models/CustomMetadataFields/CustomMetadataFieldDeleteParams.cs new file mode 100644 index 00000000..3d3f910d --- /dev/null +++ b/src/Imagekit/Models/CustomMetadataFields/CustomMetadataFieldDeleteParams.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Models.CustomMetadataFields; + +/// +/// This API deletes a custom metadata field. Even after deleting a custom metadata +/// field, you cannot create any new custom metadata field with the same name. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class CustomMetadataFieldDeleteParams : ParamsBase +{ + public string? ID { get; init; } + + public CustomMetadataFieldDeleteParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public CustomMetadataFieldDeleteParams( + CustomMetadataFieldDeleteParams customMetadataFieldDeleteParams + ) + : base(customMetadataFieldDeleteParams) + { + this.ID = customMetadataFieldDeleteParams.ID; + } +#pragma warning restore CS8618 + + public CustomMetadataFieldDeleteParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + CustomMetadataFieldDeleteParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + string id + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this.ID = id; + } +#pragma warning restore CS8618 + + /// + public static CustomMetadataFieldDeleteParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + string id + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + id + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["ID"] = JsonSerializer.SerializeToElement(this.ID), + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(CustomMetadataFieldDeleteParams? other) + { + if (other == null) + { + return false; + } + return (this.ID?.Equals(other.ID) ?? other.ID == null) + && this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + + string.Format("/v1/customMetadataFields/{0}", this.ID) + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/CustomMetadataFields/CustomMetadataFieldDeleteResponse.cs b/src/Imagekit/Models/CustomMetadataFields/CustomMetadataFieldDeleteResponse.cs new file mode 100644 index 00000000..e637f5a2 --- /dev/null +++ b/src/Imagekit/Models/CustomMetadataFields/CustomMetadataFieldDeleteResponse.cs @@ -0,0 +1,59 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models.CustomMetadataFields; + +[JsonConverter( + typeof(JsonModelConverter< + CustomMetadataFieldDeleteResponse, + CustomMetadataFieldDeleteResponseFromRaw + >) +)] +public sealed record class CustomMetadataFieldDeleteResponse : JsonModel +{ + /// + public override void Validate() { } + + public CustomMetadataFieldDeleteResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public CustomMetadataFieldDeleteResponse( + CustomMetadataFieldDeleteResponse customMetadataFieldDeleteResponse + ) + : base(customMetadataFieldDeleteResponse) { } +#pragma warning restore CS8618 + + public CustomMetadataFieldDeleteResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + CustomMetadataFieldDeleteResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static CustomMetadataFieldDeleteResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class CustomMetadataFieldDeleteResponseFromRaw : IFromRawJson +{ + /// + public CustomMetadataFieldDeleteResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) => CustomMetadataFieldDeleteResponse.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/CustomMetadataFields/CustomMetadataFieldListParams.cs b/src/Imagekit/Models/CustomMetadataFields/CustomMetadataFieldListParams.cs new file mode 100644 index 00000000..aa30b423 --- /dev/null +++ b/src/Imagekit/Models/CustomMetadataFields/CustomMetadataFieldListParams.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Models.CustomMetadataFields; + +/// +/// This API returns the array of created custom metadata field objects. By default +/// the API returns only non deleted field objects, but you can include deleted fields +/// in the API response. +/// +/// You can also filter results by a specific folder path to retrieve custom +/// metadata fields applicable at that location. This path-specific filtering is useful +/// when using the **Path policy** feature to determine which custom metadata fields +/// are selected for a given path. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class CustomMetadataFieldListParams : ParamsBase +{ + /// + /// The folder path (e.g., `/path/to/folder`) for which to retrieve applicable + /// custom metadata fields. Useful for determining path-specific field selections + /// when the [Path policy](https://imagekit.io/docs/dam/path-policy) feature + /// is in use. + /// + public string? FolderPath + { + get + { + this._rawQueryData.Freeze(); + return this._rawQueryData.GetNullableClass("folderPath"); + } + init + { + if (value == null) + { + return; + } + + this._rawQueryData.Set("folderPath", value); + } + } + + /// + /// Set it to `true` to include deleted field objects in the API response. + /// + public bool? IncludeDeleted + { + get + { + this._rawQueryData.Freeze(); + return this._rawQueryData.GetNullableStruct("includeDeleted"); + } + init + { + if (value == null) + { + return; + } + + this._rawQueryData.Set("includeDeleted", value); + } + } + + public CustomMetadataFieldListParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public CustomMetadataFieldListParams( + CustomMetadataFieldListParams customMetadataFieldListParams + ) + : base(customMetadataFieldListParams) { } +#pragma warning restore CS8618 + + public CustomMetadataFieldListParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + CustomMetadataFieldListParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } +#pragma warning restore CS8618 + + /// + public static CustomMetadataFieldListParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(CustomMetadataFieldListParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder(options.BaseUrl.ToString().TrimEnd('/') + "/v1/customMetadataFields") + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/CustomMetadataFields/CustomMetadataFieldUpdateParams.cs b/src/Imagekit/Models/CustomMetadataFields/CustomMetadataFieldUpdateParams.cs new file mode 100644 index 00000000..b9c75688 --- /dev/null +++ b/src/Imagekit/Models/CustomMetadataFields/CustomMetadataFieldUpdateParams.cs @@ -0,0 +1,1968 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; +using Text = System.Text; + +namespace Imagekit.Models.CustomMetadataFields; + +/// +/// This API updates the label or schema of an existing custom metadata field. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class CustomMetadataFieldUpdateParams : ParamsBase +{ + readonly JsonDictionary _rawBodyData = new(); + public IReadOnlyDictionary RawBodyData + { + get { return this._rawBodyData.Freeze(); } + } + + public string? ID { get; init; } + + /// + /// Human readable name of the custom metadata field. This should be unique across + /// all non deleted custom metadata fields. This name is displayed as form field + /// label to the users while setting field value on an asset in the media library + /// UI. This parameter is required if `schema` is not provided. + /// + public string? Label + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("label"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("label", value); + } + } + + /// + /// An object that describes the rules for the custom metadata key. This parameter + /// is required if `label` is not provided. Note: `type` cannot be updated and + /// will be ignored if sent with the `schema`. The schema will be validated as + /// per the existing `type`. + /// + public CustomMetadataFieldUpdateParamsSchema? Schema + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass( + "schema" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("schema", value); + } + } + + public CustomMetadataFieldUpdateParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public CustomMetadataFieldUpdateParams( + CustomMetadataFieldUpdateParams customMetadataFieldUpdateParams + ) + : base(customMetadataFieldUpdateParams) + { + this.ID = customMetadataFieldUpdateParams.ID; + + this._rawBodyData = new(customMetadataFieldUpdateParams._rawBodyData); + } +#pragma warning restore CS8618 + + public CustomMetadataFieldUpdateParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + CustomMetadataFieldUpdateParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + FrozenDictionary rawBodyData, + string id + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + this.ID = id; + } +#pragma warning restore CS8618 + + /// + public static CustomMetadataFieldUpdateParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData, + string id + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + FrozenDictionary.ToFrozenDictionary(rawBodyData), + id + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["ID"] = JsonSerializer.SerializeToElement(this.ID), + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this._rawBodyData.Freeze()), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(CustomMetadataFieldUpdateParams? other) + { + if (other == null) + { + return false; + } + return (this.ID?.Equals(other.ID) ?? other.ID == null) + && this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this._rawBodyData.Equals(other._rawBodyData); + } + + public override System::Uri Url(ClientOptions options) + { + return new System::UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + + string.Format("/v1/customMetadataFields/{0}", this.ID) + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} + +/// +/// An object that describes the rules for the custom metadata key. This parameter +/// is required if `label` is not provided. Note: `type` cannot be updated and will +/// be ignored if sent with the `schema`. The schema will be validated as per the +/// existing `type`. +/// +[JsonConverter( + typeof(JsonModelConverter< + CustomMetadataFieldUpdateParamsSchema, + CustomMetadataFieldUpdateParamsSchemaFromRaw + >) +)] +public sealed record class CustomMetadataFieldUpdateParamsSchema : JsonModel +{ + /// + /// The default value for this custom metadata field. This property is only required + /// if `isValueRequired` property is set to `true`. The value should match the + /// `type` of custom metadata field. + /// + public CustomMetadataFieldUpdateParamsSchemaDefaultValue? DefaultValue + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass( + "defaultValue" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("defaultValue", value); + } + } + + /// + /// Sets this custom metadata field as required. Setting custom metadata fields + /// on an asset will throw error if the value for all required fields are not + /// present in upload or update asset API request body. + /// + public bool? IsValueRequired + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("isValueRequired"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("isValueRequired", value); + } + } + + /// + /// Maximum length of string. Only set this property if `type` is set to `Text` + /// or `Textarea`. + /// + public double? MaxLength + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("maxLength"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("maxLength", value); + } + } + + /// + /// Maximum value of the field. Only set this property if field type is `Date` + /// or `Number`. For `Date` type field, set the minimum date in ISO8601 string + /// format. For `Number` type field, set the minimum numeric value. + /// + public CustomMetadataFieldUpdateParamsSchemaMaxValue? MaxValue + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass( + "maxValue" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("maxValue", value); + } + } + + /// + /// Minimum length of string. Only set this property if `type` is set to `Text` + /// or `Textarea`. + /// + public double? MinLength + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("minLength"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("minLength", value); + } + } + + /// + /// Minimum value of the field. Only set this property if field type is `Date` + /// or `Number`. For `Date` type field, set the minimum date in ISO8601 string + /// format. For `Number` type field, set the minimum numeric value. + /// + public CustomMetadataFieldUpdateParamsSchemaMinValue? MinValue + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass( + "minValue" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("minValue", value); + } + } + + /// + /// An array of allowed values. This property is only required if `type` property + /// is set to `SingleSelect` or `MultiSelect`. + /// + public IReadOnlyList? SelectOptions + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct< + ImmutableArray + >("selectOptions"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "selectOptions", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + this.DefaultValue?.Validate(); + _ = this.IsValueRequired; + _ = this.MaxLength; + this.MaxValue?.Validate(); + _ = this.MinLength; + this.MinValue?.Validate(); + foreach (var item in this.SelectOptions ?? []) + { + item.Validate(); + } + } + + public CustomMetadataFieldUpdateParamsSchema() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public CustomMetadataFieldUpdateParamsSchema( + CustomMetadataFieldUpdateParamsSchema customMetadataFieldUpdateParamsSchema + ) + : base(customMetadataFieldUpdateParamsSchema) { } +#pragma warning restore CS8618 + + public CustomMetadataFieldUpdateParamsSchema(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + CustomMetadataFieldUpdateParamsSchema(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static CustomMetadataFieldUpdateParamsSchema FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class CustomMetadataFieldUpdateParamsSchemaFromRaw + : IFromRawJson +{ + /// + public CustomMetadataFieldUpdateParamsSchema FromRawUnchecked( + IReadOnlyDictionary rawData + ) => CustomMetadataFieldUpdateParamsSchema.FromRawUnchecked(rawData); +} + +/// +/// The default value for this custom metadata field. This property is only required +/// if `isValueRequired` property is set to `true`. The value should match the `type` +/// of custom metadata field. +/// +[JsonConverter(typeof(CustomMetadataFieldUpdateParamsSchemaDefaultValueConverter))] +public record class CustomMetadataFieldUpdateParamsSchemaDefaultValue : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public CustomMetadataFieldUpdateParamsSchemaDefaultValue( + string value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldUpdateParamsSchemaDefaultValue( + double value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldUpdateParamsSchemaDefaultValue( + bool value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldUpdateParamsSchemaDefaultValue( + IReadOnlyList value, + JsonElement? element = null + ) + { + this.Value = ImmutableArray.ToImmutableArray(value); + this._element = element; + } + + public CustomMetadataFieldUpdateParamsSchemaDefaultValue(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type where T is a CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem. + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickMixed(out var value)) { + /// // `value` is of type `IReadOnlyList<CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem>` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickMixed( + [NotNullWhen(true)] + out IReadOnlyList? value + ) + { + value = + this.Value + as IReadOnlyList; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...}, + /// (IReadOnlyList<CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem> value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool, + System::Action< + IReadOnlyList + > mixed + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + case IReadOnlyList value: + mixed(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldUpdateParamsSchemaDefaultValue" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...}, + /// (IReadOnlyList<CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem> value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool, + System::Func< + IReadOnlyList, + T + > mixed + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + IReadOnlyList value => + mixed(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldUpdateParamsSchemaDefaultValue" + ), + }; + } + + public static implicit operator CustomMetadataFieldUpdateParamsSchemaDefaultValue( + string value + ) => new(value); + + public static implicit operator CustomMetadataFieldUpdateParamsSchemaDefaultValue( + double value + ) => new(value); + + public static implicit operator CustomMetadataFieldUpdateParamsSchemaDefaultValue(bool value) => + new(value); + + public static implicit operator CustomMetadataFieldUpdateParamsSchemaDefaultValue( + List value + ) => + new( + (IReadOnlyList)value + ); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldUpdateParamsSchemaDefaultValue" + ); + } + this.Switch( + (_) => { }, + (_) => { }, + (_) => { }, + (mixed) => + { + foreach (var item in mixed) + { + item.Validate(); + } + } + ); + } + + public virtual bool Equals(CustomMetadataFieldUpdateParamsSchemaDefaultValue? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + IReadOnlyList _ => 3, + _ => -1, + }; + } +} + +sealed class CustomMetadataFieldUpdateParamsSchemaDefaultValueConverter + : JsonConverter +{ + public override CustomMetadataFieldUpdateParamsSchemaDefaultValue? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize< + List + >(element, options); + if (deserialized != null) + { + foreach (var item in deserialized) + { + item.Validate(); + } + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + CustomMetadataFieldUpdateParamsSchemaDefaultValue value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItemConverter))] +public record class CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem( + string value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem( + double value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem( + bool value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem" + ), + }; + } + + public static implicit operator CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem( + string value + ) => new(value); + + public static implicit operator CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem( + double value + ) => new(value); + + public static implicit operator CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem( + bool value + ) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem" + ); + } + } + + public virtual bool Equals( + CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem? other + ) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + _ => -1, + }; + } +} + +sealed class CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItemConverter + : JsonConverter +{ + public override CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + CustomMetadataFieldUpdateParamsSchemaDefaultValueDefaultValueItem value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Maximum value of the field. Only set this property if field type is `Date` or +/// `Number`. For `Date` type field, set the minimum date in ISO8601 string format. +/// For `Number` type field, set the minimum numeric value. +/// +[JsonConverter(typeof(CustomMetadataFieldUpdateParamsSchemaMaxValueConverter))] +public record class CustomMetadataFieldUpdateParamsSchemaMaxValue : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public CustomMetadataFieldUpdateParamsSchemaMaxValue(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldUpdateParamsSchemaMaxValue(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldUpdateParamsSchemaMaxValue(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @string, System::Action @double) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldUpdateParamsSchemaMaxValue" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @string, System::Func @double) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldUpdateParamsSchemaMaxValue" + ), + }; + } + + public static implicit operator CustomMetadataFieldUpdateParamsSchemaMaxValue(string value) => + new(value); + + public static implicit operator CustomMetadataFieldUpdateParamsSchemaMaxValue(double value) => + new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldUpdateParamsSchemaMaxValue" + ); + } + } + + public virtual bool Equals(CustomMetadataFieldUpdateParamsSchemaMaxValue? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + _ => -1, + }; + } +} + +sealed class CustomMetadataFieldUpdateParamsSchemaMaxValueConverter + : JsonConverter +{ + public override CustomMetadataFieldUpdateParamsSchemaMaxValue? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + CustomMetadataFieldUpdateParamsSchemaMaxValue value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Minimum value of the field. Only set this property if field type is `Date` or +/// `Number`. For `Date` type field, set the minimum date in ISO8601 string format. +/// For `Number` type field, set the minimum numeric value. +/// +[JsonConverter(typeof(CustomMetadataFieldUpdateParamsSchemaMinValueConverter))] +public record class CustomMetadataFieldUpdateParamsSchemaMinValue : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public CustomMetadataFieldUpdateParamsSchemaMinValue(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldUpdateParamsSchemaMinValue(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldUpdateParamsSchemaMinValue(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @string, System::Action @double) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldUpdateParamsSchemaMinValue" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @string, System::Func @double) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldUpdateParamsSchemaMinValue" + ), + }; + } + + public static implicit operator CustomMetadataFieldUpdateParamsSchemaMinValue(string value) => + new(value); + + public static implicit operator CustomMetadataFieldUpdateParamsSchemaMinValue(double value) => + new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldUpdateParamsSchemaMinValue" + ); + } + } + + public virtual bool Equals(CustomMetadataFieldUpdateParamsSchemaMinValue? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + _ => -1, + }; + } +} + +sealed class CustomMetadataFieldUpdateParamsSchemaMinValueConverter + : JsonConverter +{ + public override CustomMetadataFieldUpdateParamsSchemaMinValue? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + CustomMetadataFieldUpdateParamsSchemaMinValue value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(CustomMetadataFieldUpdateParamsSchemaSelectOptionConverter))] +public record class CustomMetadataFieldUpdateParamsSchemaSelectOption : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public CustomMetadataFieldUpdateParamsSchemaSelectOption( + string value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldUpdateParamsSchemaSelectOption( + double value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldUpdateParamsSchemaSelectOption( + bool value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public CustomMetadataFieldUpdateParamsSchemaSelectOption(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldUpdateParamsSchemaSelectOption" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldUpdateParamsSchemaSelectOption" + ), + }; + } + + public static implicit operator CustomMetadataFieldUpdateParamsSchemaSelectOption( + string value + ) => new(value); + + public static implicit operator CustomMetadataFieldUpdateParamsSchemaSelectOption( + double value + ) => new(value); + + public static implicit operator CustomMetadataFieldUpdateParamsSchemaSelectOption(bool value) => + new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of CustomMetadataFieldUpdateParamsSchemaSelectOption" + ); + } + } + + public virtual bool Equals(CustomMetadataFieldUpdateParamsSchemaSelectOption? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + _ => -1, + }; + } +} + +sealed class CustomMetadataFieldUpdateParamsSchemaSelectOptionConverter + : JsonConverter +{ + public override CustomMetadataFieldUpdateParamsSchemaSelectOption? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + CustomMetadataFieldUpdateParamsSchemaSelectOption value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} diff --git a/src/Imagekit/Models/Dummy/DummyCreateParams.cs b/src/Imagekit/Models/Dummy/DummyCreateParams.cs new file mode 100644 index 00000000..e7a2e7a0 --- /dev/null +++ b/src/Imagekit/Models/Dummy/DummyCreateParams.cs @@ -0,0 +1,581 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; +using Text = System.Text; + +namespace Imagekit.Models.Dummy; + +/// +/// Internal test endpoint for SDK generation purposes only. This endpoint demonstrates +/// usage of all shared models defined in the Stainless configuration and is not +/// intended for public consumption. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class DummyCreateParams : ParamsBase +{ + readonly JsonDictionary _rawBodyData = new(); + public IReadOnlyDictionary RawBodyData + { + get { return this._rawBodyData.Freeze(); } + } + + public BaseOverlay? BaseOverlay + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("baseOverlay"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("baseOverlay", value); + } + } + + /// + /// Configuration object for an extension (base extensions only, not saved extension references). + /// + public ExtensionConfig? ExtensionConfig + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("extensionConfig"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("extensionConfig", value); + } + } + + /// + /// Array of extensions to be applied to the asset. Each extension can be configured + /// with specific parameters based on the extension type. + /// + public IReadOnlyList? Extensions + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct>("extensions"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set?>( + "extensions", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Options for generating responsive image attributes including `src`, `srcSet`, + /// and `sizes` for HTML `<img>` elements. This schema extends `SrcOptions` + /// to add support for responsive image generation with breakpoints. + /// + public GetImageAttributesOptions? GetImageAttributesOptions + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass( + "getImageAttributesOptions" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("getImageAttributesOptions", value); + } + } + + public ImageOverlay? ImageOverlay + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("imageOverlay"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("imageOverlay", value); + } + } + + /// + /// Specifies an overlay to be applied on the parent image or video. ImageKit + /// supports overlays including images, text, videos, subtitles, and solid colors. + /// See [Overlay using layers](https://imagekit.io/docs/transformations#overlay-using-layers). + /// + public Overlay? Overlay + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("overlay"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("overlay", value); + } + } + + public OverlayPosition? OverlayPosition + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("overlayPosition"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("overlayPosition", value); + } + } + + public OverlayTiming? OverlayTiming + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("overlayTiming"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("overlayTiming", value); + } + } + + /// + /// Resulting set of attributes suitable for an HTML `<img>` element. Useful + /// for enabling responsive image loading with `srcSet` and `sizes`. + /// + public ResponsiveImageAttributes? ResponsiveImageAttributes + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass( + "responsiveImageAttributes" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("responsiveImageAttributes", value); + } + } + + /// + /// Saved extension object containing extension configuration. + /// + public SharedSavedExtension? SavedExtensions + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("savedExtensions"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("savedExtensions", value); + } + } + + public SolidColorOverlay? SolidColorOverlay + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("solidColorOverlay"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("solidColorOverlay", value); + } + } + + public SolidColorOverlayTransformation? SolidColorOverlayTransformation + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass( + "solidColorOverlayTransformation" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("solidColorOverlayTransformation", value); + } + } + + /// + /// Options for generating ImageKit URLs with transformations. See the [Transformations guide](https://imagekit.io/docs/transformations). + /// + public SrcOptions? SrcOptions + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("srcOptions"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("srcOptions", value); + } + } + + /// + /// Available streaming resolutions for [adaptive bitrate streaming](https://imagekit.io/docs/adaptive-bitrate-streaming) + /// + public ApiEnum? StreamingResolution + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass>( + "streamingResolution" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("streamingResolution", value); + } + } + + public SubtitleOverlay? SubtitleOverlay + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("subtitleOverlay"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("subtitleOverlay", value); + } + } + + /// + /// Subtitle styling options. [Learn more](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + /// from the docs. + /// + public SubtitleOverlayTransformation? SubtitleOverlayTransformation + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass( + "subtitleOverlayTransformation" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("subtitleOverlayTransformation", value); + } + } + + public TextOverlay? TextOverlay + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("textOverlay"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("textOverlay", value); + } + } + + public TextOverlayTransformation? TextOverlayTransformation + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass( + "textOverlayTransformation" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("textOverlayTransformation", value); + } + } + + /// + /// The SDK provides easy-to-use names for transformations. These names are converted + /// to the corresponding transformation string before being added to the URL. + /// SDKs are updated regularly to support new transformations. If you want to + /// use a transformation that is not supported by the SDK, You can use the `raw` + /// parameter to pass the transformation string directly. See the [Transformations + /// documentation](https://imagekit.io/docs/transformations). + /// + public Transformation? Transformation + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("transformation"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("transformation", value); + } + } + + /// + /// By default, the transformation string is added as a query parameter in the + /// URL, e.g., `?tr=w-100,h-100`. If you want to add the transformation string + /// in the path of the URL, set this to `path`. Learn more in the [Transformations + /// guide](https://imagekit.io/docs/transformations). + /// + public ApiEnum? TransformationPosition + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass>( + "transformationPosition" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("transformationPosition", value); + } + } + + public VideoOverlay? VideoOverlay + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("videoOverlay"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("videoOverlay", value); + } + } + + public DummyCreateParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public DummyCreateParams(DummyCreateParams dummyCreateParams) + : base(dummyCreateParams) + { + this._rawBodyData = new(dummyCreateParams._rawBodyData); + } +#pragma warning restore CS8618 + + public DummyCreateParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + DummyCreateParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + FrozenDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } +#pragma warning restore CS8618 + + /// + public static DummyCreateParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + FrozenDictionary.ToFrozenDictionary(rawBodyData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this._rawBodyData.Freeze()), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(DummyCreateParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this._rawBodyData.Equals(other._rawBodyData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder(options.BaseUrl.ToString().TrimEnd('/') + "/v1/dummy/test") + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/ExtensionConfig.cs b/src/Imagekit/Models/ExtensionConfig.cs new file mode 100644 index 00000000..4dd89d7a --- /dev/null +++ b/src/Imagekit/Models/ExtensionConfig.cs @@ -0,0 +1,4972 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models; + +/// +/// Configuration object for an extension (base extensions only, not saved extension references). +/// +[JsonConverter(typeof(ExtensionConfigConverter))] +public record class ExtensionConfig : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public ExtensionConfig(RemoveBg value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public ExtensionConfig(AutoTaggingExtension value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public ExtensionConfig(AIAutoDescription value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public ExtensionConfig(AITasks value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public ExtensionConfig(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickRemoveBg(out var value)) { + /// // `value` is of type `RemoveBg` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickRemoveBg([NotNullWhen(true)] out RemoveBg? value) + { + value = this.Value as RemoveBg; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickAutoTaggingExtension(out var value)) { + /// // `value` is of type `AutoTaggingExtension` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickAutoTaggingExtension([NotNullWhen(true)] out AutoTaggingExtension? value) + { + value = this.Value as AutoTaggingExtension; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickAIAutoDescription(out var value)) { + /// // `value` is of type `AIAutoDescription` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickAIAutoDescription([NotNullWhen(true)] out AIAutoDescription? value) + { + value = this.Value as AIAutoDescription; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickAITasks(out var value)) { + /// // `value` is of type `AITasks` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickAITasks([NotNullWhen(true)] out AITasks? value) + { + value = this.Value as AITasks; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (RemoveBg value) => {...}, + /// (AutoTaggingExtension value) => {...}, + /// (AIAutoDescription value) => {...}, + /// (AITasks value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action removeBg, + System::Action autoTaggingExtension, + System::Action aiAutoDescription, + System::Action aiTasks + ) + { + switch (this.Value) + { + case RemoveBg value: + removeBg(value); + break; + case AutoTaggingExtension value: + autoTaggingExtension(value); + break; + case AIAutoDescription value: + aiAutoDescription(value); + break; + case AITasks value: + aiTasks(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionConfig" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (RemoveBg value) => {...}, + /// (AutoTaggingExtension value) => {...}, + /// (AIAutoDescription value) => {...}, + /// (AITasks value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func removeBg, + System::Func autoTaggingExtension, + System::Func aiAutoDescription, + System::Func aiTasks + ) + { + return this.Value switch + { + RemoveBg value => removeBg(value), + AutoTaggingExtension value => autoTaggingExtension(value), + AIAutoDescription value => aiAutoDescription(value), + AITasks value => aiTasks(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionConfig" + ), + }; + } + + public static implicit operator ExtensionConfig(RemoveBg value) => new(value); + + public static implicit operator ExtensionConfig(AutoTaggingExtension value) => new(value); + + public static implicit operator ExtensionConfig(AIAutoDescription value) => new(value); + + public static implicit operator ExtensionConfig(AITasks value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionConfig" + ); + } + this.Switch( + (removeBg) => removeBg.Validate(), + (autoTaggingExtension) => autoTaggingExtension.Validate(), + (aiAutoDescription) => aiAutoDescription.Validate(), + (aiTasks) => aiTasks.Validate() + ); + } + + public virtual bool Equals(ExtensionConfig? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + RemoveBg _ => 0, + AutoTaggingExtension _ => 1, + AIAutoDescription _ => 2, + AITasks _ => 3, + _ => -1, + }; + } +} + +sealed class ExtensionConfigConverter : JsonConverter +{ + public override ExtensionConfig? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + string? name; + try + { + name = element.GetProperty("name").GetString(); + } + catch + { + name = null; + } + + switch (name) + { + case "remove-bg": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "ai-auto-description": + { + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "ai-tasks": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + default: + { + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + } + } + + public override void Write( + Utf8JsonWriter writer, + ExtensionConfig value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class RemoveBg : JsonModel +{ + /// + /// Specifies the background removal extension. + /// + public JsonElement Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("name"); + } + init { this._rawData.Set("name", value); } + } + + public Options? Options + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("options"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("options", value); + } + } + + /// + public override void Validate() + { + if (!JsonElement.DeepEquals(this.Name, JsonSerializer.SerializeToElement("remove-bg"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + this.Options?.Validate(); + } + + public RemoveBg() + { + this.Name = JsonSerializer.SerializeToElement("remove-bg"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public RemoveBg(RemoveBg removeBg) + : base(removeBg) { } +#pragma warning restore CS8618 + + public RemoveBg(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Name = JsonSerializer.SerializeToElement("remove-bg"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + RemoveBg(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static RemoveBg FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class RemoveBgFromRaw : IFromRawJson +{ + /// + public RemoveBg FromRawUnchecked(IReadOnlyDictionary rawData) => + RemoveBg.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Options : JsonModel +{ + /// + /// Whether to add an artificial shadow to the result. Default is false. Note: + /// Adding shadows is currently only supported for car photos. + /// + public bool? AddShadow + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("add_shadow"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("add_shadow", value); + } + } + + /// + /// Specifies a solid color background using hex code (e.g., "81d4fa", "fff") + /// or color name (e.g., "green"). If this parameter is set, `bg_image_url` must + /// be empty. + /// + public string? BgColor + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("bg_color"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("bg_color", value); + } + } + + /// + /// Sets a background image from a URL. If this parameter is set, `bg_color` must + /// be empty. + /// + public string? BgImageUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("bg_image_url"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("bg_image_url", value); + } + } + + /// + /// Allows semi-transparent regions in the result. Default is true. Note: Semitransparency + /// is currently only supported for car windows. + /// + public bool? Semitransparency + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("semitransparency"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("semitransparency", value); + } + } + + /// + public override void Validate() + { + _ = this.AddShadow; + _ = this.BgColor; + _ = this.BgImageUrl; + _ = this.Semitransparency; + } + + public Options() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Options(Options options) + : base(options) { } +#pragma warning restore CS8618 + + public Options(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Options(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Options FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class OptionsFromRaw : IFromRawJson +{ + /// + public Options FromRawUnchecked(IReadOnlyDictionary rawData) => + Options.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class AutoTaggingExtension : JsonModel +{ + /// + /// Maximum number of tags to attach to the asset. + /// + public required long MaxTags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("maxTags"); + } + init { this._rawData.Set("maxTags", value); } + } + + /// + /// Minimum confidence level for tags to be considered valid. + /// + public required long MinConfidence + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("minConfidence"); + } + init { this._rawData.Set("minConfidence", value); } + } + + /// + /// Specifies the auto-tagging extension used. + /// + public required ApiEnum Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass>("name"); + } + init { this._rawData.Set("name", value); } + } + + /// + public override void Validate() + { + _ = this.MaxTags; + _ = this.MinConfidence; + this.Name.Validate(); + } + + public AutoTaggingExtension() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public AutoTaggingExtension(AutoTaggingExtension autoTaggingExtension) + : base(autoTaggingExtension) { } +#pragma warning restore CS8618 + + public AutoTaggingExtension(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + AutoTaggingExtension(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static AutoTaggingExtension FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class AutoTaggingExtensionFromRaw : IFromRawJson +{ + /// + public AutoTaggingExtension FromRawUnchecked( + IReadOnlyDictionary rawData + ) => AutoTaggingExtension.FromRawUnchecked(rawData); +} + +/// +/// Specifies the auto-tagging extension used. +/// +[JsonConverter(typeof(NameConverter))] +public enum Name +{ + GoogleAutoTagging, + AwsAutoTagging, +} + +sealed class NameConverter : JsonConverter +{ + public override Name Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "google-auto-tagging" => Name.GoogleAutoTagging, + "aws-auto-tagging" => Name.AwsAutoTagging, + _ => (Name)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, Name value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + Name.GoogleAutoTagging => "google-auto-tagging", + Name.AwsAutoTagging => "aws-auto-tagging", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +[JsonConverter(typeof(AIAutoDescriptionConverter))] +public record class AIAutoDescription +{ + public JsonElement Element { get; private init; } + + public AIAutoDescription() + { + Element = JsonSerializer.Deserialize( + """ + { + "name": "ai-auto-description" + } + """ + ); + } + + internal AIAutoDescription(JsonElement element) + { + Element = element; + } + + /// + /// Validates that the instance's underlying value is the expected constant. + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public void Validate() + { + if (this != new AIAutoDescription()) + { + throw new ImageKitInvalidDataException("Invalid value given for 'AIAutoDescription'"); + } + } + + public override int GetHashCode() + { + return 0; + } + + public virtual bool Equals(AIAutoDescription? other) + { + if (other == null) + { + return false; + } + + return JsonElement.DeepEquals(this.Element, other.Element); + } +} + +class AIAutoDescriptionConverter : JsonConverter +{ + public override AIAutoDescription? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return new(JsonSerializer.Deserialize(ref reader, options)); + } + + public override void Write( + Utf8JsonWriter writer, + AIAutoDescription value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Element, options); + } +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class AITasks : JsonModel +{ + /// + /// Specifies the AI tasks extension for automated image analysis using AI models. + /// + public JsonElement Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("name"); + } + init { this._rawData.Set("name", value); } + } + + /// + /// Array of task objects defining AI operations to perform on the asset. + /// + public required IReadOnlyList Tasks + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct>("tasks"); + } + init + { + this._rawData.Set>( + "tasks", + ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + if (!JsonElement.DeepEquals(this.Name, JsonSerializer.SerializeToElement("ai-tasks"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + foreach (var item in this.Tasks) + { + item.Validate(); + } + } + + public AITasks() + { + this.Name = JsonSerializer.SerializeToElement("ai-tasks"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public AITasks(AITasks aiTasks) + : base(aiTasks) { } +#pragma warning restore CS8618 + + public AITasks(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Name = JsonSerializer.SerializeToElement("ai-tasks"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + AITasks(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static AITasks FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public AITasks(IReadOnlyList tasks) + : this() + { + this.Tasks = tasks; + } +} + +class AITasksFromRaw : IFromRawJson +{ + /// + public AITasks FromRawUnchecked(IReadOnlyDictionary rawData) => + AITasks.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(TaskConverter))] +public record class Task : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public string Instruction + { + get + { + return Match( + selectTags: (x) => x.Instruction, + selectMetadata: (x) => x.Instruction, + yesNo: (x) => x.Instruction + ); + } + } + + public JsonElement Type + { + get + { + return Match( + selectTags: (x) => x.Type, + selectMetadata: (x) => x.Type, + yesNo: (x) => x.Type + ); + } + } + + public long? MaxSelections + { + get + { + return Match( + selectTags: (x) => x.MaxSelections, + selectMetadata: (x) => x.MaxSelections, + yesNo: (_) => null + ); + } + } + + public long? MinSelections + { + get + { + return Match( + selectTags: (x) => x.MinSelections, + selectMetadata: (x) => x.MinSelections, + yesNo: (_) => null + ); + } + } + + public Task(SelectTags value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Task(SelectMetadata value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Task(YesNo value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Task(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickSelectTags(out var value)) { + /// // `value` is of type `SelectTags` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickSelectTags([NotNullWhen(true)] out SelectTags? value) + { + value = this.Value as SelectTags; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickSelectMetadata(out var value)) { + /// // `value` is of type `SelectMetadata` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickSelectMetadata([NotNullWhen(true)] out SelectMetadata? value) + { + value = this.Value as SelectMetadata; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickYesNo(out var value)) { + /// // `value` is of type `YesNo` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickYesNo([NotNullWhen(true)] out YesNo? value) + { + value = this.Value as YesNo; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (SelectTags value) => {...}, + /// (SelectMetadata value) => {...}, + /// (YesNo value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action selectTags, + System::Action selectMetadata, + System::Action yesNo + ) + { + switch (this.Value) + { + case SelectTags value: + selectTags(value); + break; + case SelectMetadata value: + selectMetadata(value); + break; + case YesNo value: + yesNo(value); + break; + default: + throw new ImageKitInvalidDataException("Data did not match any variant of Task"); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (SelectTags value) => {...}, + /// (SelectMetadata value) => {...}, + /// (YesNo value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func selectTags, + System::Func selectMetadata, + System::Func yesNo + ) + { + return this.Value switch + { + SelectTags value => selectTags(value), + SelectMetadata value => selectMetadata(value), + YesNo value => yesNo(value), + _ => throw new ImageKitInvalidDataException("Data did not match any variant of Task"), + }; + } + + public static implicit operator Task(SelectTags value) => new(value); + + public static implicit operator Task(SelectMetadata value) => new(value); + + public static implicit operator Task(YesNo value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of Task"); + } + this.Switch( + (selectTags) => selectTags.Validate(), + (selectMetadata) => selectMetadata.Validate(), + (yesNo) => yesNo.Validate() + ); + } + + public virtual bool Equals(Task? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + SelectTags _ => 0, + SelectMetadata _ => 1, + YesNo _ => 2, + _ => -1, + }; + } +} + +sealed class TaskConverter : JsonConverter +{ + public override Task? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + string? type; + try + { + type = element.GetProperty("type").GetString(); + } + catch + { + type = null; + } + + switch (type) + { + case "select_tags": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "select_metadata": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "yes_no": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + default: + { + return new Task(element); + } + } + } + + public override void Write(Utf8JsonWriter writer, Task value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class SelectTags : JsonModel +{ + /// + /// The question or instruction for the AI to analyze the image. + /// + public required string Instruction + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("instruction"); + } + init { this._rawData.Set("instruction", value); } + } + + /// + /// Task type that analyzes the image and adds matching tags from a vocabulary. + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Maximum number of tags to select from the vocabulary. + /// + public long? MaxSelections + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("max_selections"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("max_selections", value); + } + } + + /// + /// Minimum number of tags to select from the vocabulary. + /// + public long? MinSelections + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("min_selections"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("min_selections", value); + } + } + + /// + /// Array of possible tag values. The combined length of all strings must not + /// exceed 500 characters, and values cannot include the `%` character. When + /// providing large vocabularies (more than 30 items), the AI may not follow + /// the list strictly. + /// + public IReadOnlyList? Vocabulary + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("vocabulary"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "vocabulary", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.Instruction; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("select_tags"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.MaxSelections; + _ = this.MinSelections; + _ = this.Vocabulary; + } + + public SelectTags() + { + this.Type = JsonSerializer.SerializeToElement("select_tags"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SelectTags(SelectTags selectTags) + : base(selectTags) { } +#pragma warning restore CS8618 + + public SelectTags(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("select_tags"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SelectTags(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SelectTags FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public SelectTags(string instruction) + : this() + { + this.Instruction = instruction; + } +} + +class SelectTagsFromRaw : IFromRawJson +{ + /// + public SelectTags FromRawUnchecked(IReadOnlyDictionary rawData) => + SelectTags.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class SelectMetadata : JsonModel +{ + /// + /// Name of the custom metadata field to set. The field must exist in your account. + /// + public required string Field + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("field"); + } + init { this._rawData.Set("field", value); } + } + + /// + /// The question or instruction for the AI to analyze the image. + /// + public required string Instruction + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("instruction"); + } + init { this._rawData.Set("instruction", value); } + } + + /// + /// Task type that analyzes the image and sets a custom metadata field value from + /// a vocabulary. + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Maximum number of values to select from the vocabulary. + /// + public long? MaxSelections + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("max_selections"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("max_selections", value); + } + } + + /// + /// Minimum number of values to select from the vocabulary. + /// + public long? MinSelections + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("min_selections"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("min_selections", value); + } + } + + /// + /// An array of possible values matching the custom metadata field type. If not + /// provided for SingleSelect or MultiSelect field types, all values from the + /// custom metadata field definition will be used. When providing large vocabularies + /// (above 30 items), the AI may not strictly adhere to the list. + /// + public IReadOnlyList? Vocabulary + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("vocabulary"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "vocabulary", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.Field; + _ = this.Instruction; + if ( + !JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("select_metadata")) + ) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.MaxSelections; + _ = this.MinSelections; + foreach (var item in this.Vocabulary ?? []) + { + item.Validate(); + } + } + + public SelectMetadata() + { + this.Type = JsonSerializer.SerializeToElement("select_metadata"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SelectMetadata(SelectMetadata selectMetadata) + : base(selectMetadata) { } +#pragma warning restore CS8618 + + public SelectMetadata(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("select_metadata"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SelectMetadata(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SelectMetadata FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class SelectMetadataFromRaw : IFromRawJson +{ + /// + public SelectMetadata FromRawUnchecked(IReadOnlyDictionary rawData) => + SelectMetadata.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(VocabularyConverter))] +public record class Vocabulary : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public Vocabulary(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Vocabulary(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Vocabulary(bool value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Vocabulary(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of Vocabulary" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of Vocabulary" + ), + }; + } + + public static implicit operator Vocabulary(string value) => new(value); + + public static implicit operator Vocabulary(double value) => new(value); + + public static implicit operator Vocabulary(bool value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of Vocabulary"); + } + } + + public virtual bool Equals(Vocabulary? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + _ => -1, + }; + } +} + +sealed class VocabularyConverter : JsonConverter +{ + public override Vocabulary? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + Vocabulary value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class YesNo : JsonModel +{ + /// + /// The yes/no question for the AI to answer about the image. + /// + public required string Instruction + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("instruction"); + } + init { this._rawData.Set("instruction", value); } + } + + /// + /// Task type that asks a yes/no question and executes actions based on the answer. + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Actions to execute if the AI answers no. + /// + public OnNo? OnNo + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("on_no"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("on_no", value); + } + } + + /// + /// Actions to execute if the AI cannot determine the answer. + /// + public OnUnknown? OnUnknown + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("on_unknown"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("on_unknown", value); + } + } + + /// + /// Actions to execute if the AI answers yes. + /// + public OnYes? OnYes + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("on_yes"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("on_yes", value); + } + } + + /// + public override void Validate() + { + _ = this.Instruction; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("yes_no"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + this.OnNo?.Validate(); + this.OnUnknown?.Validate(); + this.OnYes?.Validate(); + } + + public YesNo() + { + this.Type = JsonSerializer.SerializeToElement("yes_no"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public YesNo(YesNo yesNo) + : base(yesNo) { } +#pragma warning restore CS8618 + + public YesNo(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("yes_no"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + YesNo(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static YesNo FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public YesNo(string instruction) + : this() + { + this.Instruction = instruction; + } +} + +class YesNoFromRaw : IFromRawJson +{ + /// + public YesNo FromRawUnchecked(IReadOnlyDictionary rawData) => + YesNo.FromRawUnchecked(rawData); +} + +/// +/// Actions to execute if the AI answers no. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class OnNo : JsonModel +{ + /// + /// Array of tag strings to add to the asset. + /// + public IReadOnlyList? AddTags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("add_tags"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "add_tags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Array of tag strings to remove from the asset. + /// + public IReadOnlyList? RemoveTags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("remove_tags"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "remove_tags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Array of custom metadata field updates. + /// + public IReadOnlyList? SetMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("set_metadata"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "set_metadata", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Array of custom metadata fields to remove. + /// + public IReadOnlyList? UnsetMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("unset_metadata"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "unset_metadata", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.AddTags; + _ = this.RemoveTags; + foreach (var item in this.SetMetadata ?? []) + { + item.Validate(); + } + foreach (var item in this.UnsetMetadata ?? []) + { + item.Validate(); + } + } + + public OnNo() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public OnNo(OnNo onNo) + : base(onNo) { } +#pragma warning restore CS8618 + + public OnNo(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + OnNo(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static OnNo FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class OnNoFromRaw : IFromRawJson +{ + /// + public OnNo FromRawUnchecked(IReadOnlyDictionary rawData) => + OnNo.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class SetMetadata : JsonModel +{ + /// + /// Name of the custom metadata field to set. + /// + public required string Field + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("field"); + } + init { this._rawData.Set("field", value); } + } + + /// + /// Value to set for the custom metadata field. The value type should match the + /// custom metadata field type. + /// + public required SetMetadataValue Value + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("value"); + } + init { this._rawData.Set("value", value); } + } + + /// + public override void Validate() + { + _ = this.Field; + this.Value.Validate(); + } + + public SetMetadata() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SetMetadata(SetMetadata setMetadata) + : base(setMetadata) { } +#pragma warning restore CS8618 + + public SetMetadata(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SetMetadata(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SetMetadata FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class SetMetadataFromRaw : IFromRawJson +{ + /// + public SetMetadata FromRawUnchecked(IReadOnlyDictionary rawData) => + SetMetadata.FromRawUnchecked(rawData); +} + +/// +/// Value to set for the custom metadata field. The value type should match the custom +/// metadata field type. +/// +[JsonConverter(typeof(SetMetadataValueConverter))] +public record class SetMetadataValue : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public SetMetadataValue(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public SetMetadataValue(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public SetMetadataValue(bool value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public SetMetadataValue(IReadOnlyList value, JsonElement? element = null) + { + this.Value = ImmutableArray.ToImmutableArray(value); + this._element = element; + } + + public SetMetadataValue(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type where T is a MetadataValueItem. + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickMixed(out var value)) { + /// // `value` is of type `IReadOnlyList<MetadataValueItem>` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickMixed([NotNullWhen(true)] out IReadOnlyList? value) + { + value = this.Value as IReadOnlyList; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...}, + /// (IReadOnlyList<MetadataValueItem> value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool, + System::Action> mixed + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + case IReadOnlyList value: + mixed(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of SetMetadataValue" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...}, + /// (IReadOnlyList<MetadataValueItem> value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool, + System::Func, T> mixed + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + IReadOnlyList value => mixed(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of SetMetadataValue" + ), + }; + } + + public static implicit operator SetMetadataValue(string value) => new(value); + + public static implicit operator SetMetadataValue(double value) => new(value); + + public static implicit operator SetMetadataValue(bool value) => new(value); + + public static implicit operator SetMetadataValue(List value) => + new((IReadOnlyList)value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of SetMetadataValue" + ); + } + this.Switch( + (_) => { }, + (_) => { }, + (_) => { }, + (mixed) => + { + foreach (var item in mixed) + { + item.Validate(); + } + } + ); + } + + public virtual bool Equals(SetMetadataValue? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + IReadOnlyList _ => 3, + _ => -1, + }; + } +} + +sealed class SetMetadataValueConverter : JsonConverter +{ + public override SetMetadataValue? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize>( + element, + options + ); + if (deserialized != null) + { + foreach (var item in deserialized) + { + item.Validate(); + } + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + SetMetadataValue value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(MetadataValueItemConverter))] +public record class MetadataValueItem : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public MetadataValueItem(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public MetadataValueItem(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public MetadataValueItem(bool value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public MetadataValueItem(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of MetadataValueItem" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of MetadataValueItem" + ), + }; + } + + public static implicit operator MetadataValueItem(string value) => new(value); + + public static implicit operator MetadataValueItem(double value) => new(value); + + public static implicit operator MetadataValueItem(bool value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of MetadataValueItem" + ); + } + } + + public virtual bool Equals(MetadataValueItem? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + _ => -1, + }; + } +} + +sealed class MetadataValueItemConverter : JsonConverter +{ + public override MetadataValueItem? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + MetadataValueItem value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class UnsetMetadata : JsonModel +{ + /// + /// Name of the custom metadata field to remove. + /// + public required string Field + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("field"); + } + init { this._rawData.Set("field", value); } + } + + /// + public override void Validate() + { + _ = this.Field; + } + + public UnsetMetadata() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UnsetMetadata(UnsetMetadata unsetMetadata) + : base(unsetMetadata) { } +#pragma warning restore CS8618 + + public UnsetMetadata(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UnsetMetadata(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UnsetMetadata FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public UnsetMetadata(string field) + : this() + { + this.Field = field; + } +} + +class UnsetMetadataFromRaw : IFromRawJson +{ + /// + public UnsetMetadata FromRawUnchecked(IReadOnlyDictionary rawData) => + UnsetMetadata.FromRawUnchecked(rawData); +} + +/// +/// Actions to execute if the AI cannot determine the answer. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class OnUnknown : JsonModel +{ + /// + /// Array of tag strings to add to the asset. + /// + public IReadOnlyList? AddTags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("add_tags"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "add_tags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Array of tag strings to remove from the asset. + /// + public IReadOnlyList? RemoveTags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("remove_tags"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "remove_tags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Array of custom metadata field updates. + /// + public IReadOnlyList? SetMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>( + "set_metadata" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "set_metadata", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Array of custom metadata fields to remove. + /// + public IReadOnlyList? UnsetMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>( + "unset_metadata" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "unset_metadata", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.AddTags; + _ = this.RemoveTags; + foreach (var item in this.SetMetadata ?? []) + { + item.Validate(); + } + foreach (var item in this.UnsetMetadata ?? []) + { + item.Validate(); + } + } + + public OnUnknown() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public OnUnknown(OnUnknown onUnknown) + : base(onUnknown) { } +#pragma warning restore CS8618 + + public OnUnknown(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + OnUnknown(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static OnUnknown FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class OnUnknownFromRaw : IFromRawJson +{ + /// + public OnUnknown FromRawUnchecked(IReadOnlyDictionary rawData) => + OnUnknown.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class OnUnknownSetMetadata : JsonModel +{ + /// + /// Name of the custom metadata field to set. + /// + public required string Field + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("field"); + } + init { this._rawData.Set("field", value); } + } + + /// + /// Value to set for the custom metadata field. The value type should match the + /// custom metadata field type. + /// + public required OnUnknownSetMetadataValue Value + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("value"); + } + init { this._rawData.Set("value", value); } + } + + /// + public override void Validate() + { + _ = this.Field; + this.Value.Validate(); + } + + public OnUnknownSetMetadata() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public OnUnknownSetMetadata(OnUnknownSetMetadata onUnknownSetMetadata) + : base(onUnknownSetMetadata) { } +#pragma warning restore CS8618 + + public OnUnknownSetMetadata(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + OnUnknownSetMetadata(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static OnUnknownSetMetadata FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class OnUnknownSetMetadataFromRaw : IFromRawJson +{ + /// + public OnUnknownSetMetadata FromRawUnchecked( + IReadOnlyDictionary rawData + ) => OnUnknownSetMetadata.FromRawUnchecked(rawData); +} + +/// +/// Value to set for the custom metadata field. The value type should match the custom +/// metadata field type. +/// +[JsonConverter(typeof(OnUnknownSetMetadataValueConverter))] +public record class OnUnknownSetMetadataValue : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public OnUnknownSetMetadataValue(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OnUnknownSetMetadataValue(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OnUnknownSetMetadataValue(bool value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OnUnknownSetMetadataValue( + IReadOnlyList value, + JsonElement? element = null + ) + { + this.Value = ImmutableArray.ToImmutableArray(value); + this._element = element; + } + + public OnUnknownSetMetadataValue(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type where T is a OnUnknownSetMetadataValueMetadataValueItem. + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickMixed(out var value)) { + /// // `value` is of type `IReadOnlyList<OnUnknownSetMetadataValueMetadataValueItem>` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickMixed( + [NotNullWhen(true)] out IReadOnlyList? value + ) + { + value = this.Value as IReadOnlyList; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...}, + /// (IReadOnlyList<OnUnknownSetMetadataValueMetadataValueItem> value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool, + System::Action> mixed + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + case IReadOnlyList value: + mixed(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of OnUnknownSetMetadataValue" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...}, + /// (IReadOnlyList<OnUnknownSetMetadataValueMetadataValueItem> value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool, + System::Func, T> mixed + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + IReadOnlyList value => mixed(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of OnUnknownSetMetadataValue" + ), + }; + } + + public static implicit operator OnUnknownSetMetadataValue(string value) => new(value); + + public static implicit operator OnUnknownSetMetadataValue(double value) => new(value); + + public static implicit operator OnUnknownSetMetadataValue(bool value) => new(value); + + public static implicit operator OnUnknownSetMetadataValue( + List value + ) => new((IReadOnlyList)value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of OnUnknownSetMetadataValue" + ); + } + this.Switch( + (_) => { }, + (_) => { }, + (_) => { }, + (mixed) => + { + foreach (var item in mixed) + { + item.Validate(); + } + } + ); + } + + public virtual bool Equals(OnUnknownSetMetadataValue? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + IReadOnlyList _ => 3, + _ => -1, + }; + } +} + +sealed class OnUnknownSetMetadataValueConverter : JsonConverter +{ + public override OnUnknownSetMetadataValue? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize< + List + >(element, options); + if (deserialized != null) + { + foreach (var item in deserialized) + { + item.Validate(); + } + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + OnUnknownSetMetadataValue value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(OnUnknownSetMetadataValueMetadataValueItemConverter))] +public record class OnUnknownSetMetadataValueMetadataValueItem : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public OnUnknownSetMetadataValueMetadataValueItem(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OnUnknownSetMetadataValueMetadataValueItem(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OnUnknownSetMetadataValueMetadataValueItem(bool value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OnUnknownSetMetadataValueMetadataValueItem(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of OnUnknownSetMetadataValueMetadataValueItem" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of OnUnknownSetMetadataValueMetadataValueItem" + ), + }; + } + + public static implicit operator OnUnknownSetMetadataValueMetadataValueItem(string value) => + new(value); + + public static implicit operator OnUnknownSetMetadataValueMetadataValueItem(double value) => + new(value); + + public static implicit operator OnUnknownSetMetadataValueMetadataValueItem(bool value) => + new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of OnUnknownSetMetadataValueMetadataValueItem" + ); + } + } + + public virtual bool Equals(OnUnknownSetMetadataValueMetadataValueItem? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + _ => -1, + }; + } +} + +sealed class OnUnknownSetMetadataValueMetadataValueItemConverter + : JsonConverter +{ + public override OnUnknownSetMetadataValueMetadataValueItem? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + OnUnknownSetMetadataValueMetadataValueItem value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class OnUnknownUnsetMetadata : JsonModel +{ + /// + /// Name of the custom metadata field to remove. + /// + public required string Field + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("field"); + } + init { this._rawData.Set("field", value); } + } + + /// + public override void Validate() + { + _ = this.Field; + } + + public OnUnknownUnsetMetadata() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public OnUnknownUnsetMetadata(OnUnknownUnsetMetadata onUnknownUnsetMetadata) + : base(onUnknownUnsetMetadata) { } +#pragma warning restore CS8618 + + public OnUnknownUnsetMetadata(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + OnUnknownUnsetMetadata(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static OnUnknownUnsetMetadata FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public OnUnknownUnsetMetadata(string field) + : this() + { + this.Field = field; + } +} + +class OnUnknownUnsetMetadataFromRaw : IFromRawJson +{ + /// + public OnUnknownUnsetMetadata FromRawUnchecked( + IReadOnlyDictionary rawData + ) => OnUnknownUnsetMetadata.FromRawUnchecked(rawData); +} + +/// +/// Actions to execute if the AI answers yes. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class OnYes : JsonModel +{ + /// + /// Array of tag strings to add to the asset. + /// + public IReadOnlyList? AddTags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("add_tags"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "add_tags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Array of tag strings to remove from the asset. + /// + public IReadOnlyList? RemoveTags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("remove_tags"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "remove_tags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Array of custom metadata field updates. + /// + public IReadOnlyList? SetMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>( + "set_metadata" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "set_metadata", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Array of custom metadata fields to remove. + /// + public IReadOnlyList? UnsetMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>( + "unset_metadata" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "unset_metadata", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.AddTags; + _ = this.RemoveTags; + foreach (var item in this.SetMetadata ?? []) + { + item.Validate(); + } + foreach (var item in this.UnsetMetadata ?? []) + { + item.Validate(); + } + } + + public OnYes() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public OnYes(OnYes onYes) + : base(onYes) { } +#pragma warning restore CS8618 + + public OnYes(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + OnYes(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static OnYes FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class OnYesFromRaw : IFromRawJson +{ + /// + public OnYes FromRawUnchecked(IReadOnlyDictionary rawData) => + OnYes.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class OnYesSetMetadata : JsonModel +{ + /// + /// Name of the custom metadata field to set. + /// + public required string Field + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("field"); + } + init { this._rawData.Set("field", value); } + } + + /// + /// Value to set for the custom metadata field. The value type should match the + /// custom metadata field type. + /// + public required OnYesSetMetadataValue Value + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("value"); + } + init { this._rawData.Set("value", value); } + } + + /// + public override void Validate() + { + _ = this.Field; + this.Value.Validate(); + } + + public OnYesSetMetadata() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public OnYesSetMetadata(OnYesSetMetadata onYesSetMetadata) + : base(onYesSetMetadata) { } +#pragma warning restore CS8618 + + public OnYesSetMetadata(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + OnYesSetMetadata(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static OnYesSetMetadata FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class OnYesSetMetadataFromRaw : IFromRawJson +{ + /// + public OnYesSetMetadata FromRawUnchecked(IReadOnlyDictionary rawData) => + OnYesSetMetadata.FromRawUnchecked(rawData); +} + +/// +/// Value to set for the custom metadata field. The value type should match the custom +/// metadata field type. +/// +[JsonConverter(typeof(OnYesSetMetadataValueConverter))] +public record class OnYesSetMetadataValue : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public OnYesSetMetadataValue(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OnYesSetMetadataValue(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OnYesSetMetadataValue(bool value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OnYesSetMetadataValue( + IReadOnlyList value, + JsonElement? element = null + ) + { + this.Value = ImmutableArray.ToImmutableArray(value); + this._element = element; + } + + public OnYesSetMetadataValue(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type where T is a OnYesSetMetadataValueMetadataValueItem. + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickMixed(out var value)) { + /// // `value` is of type `IReadOnlyList<OnYesSetMetadataValueMetadataValueItem>` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickMixed( + [NotNullWhen(true)] out IReadOnlyList? value + ) + { + value = this.Value as IReadOnlyList; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...}, + /// (IReadOnlyList<OnYesSetMetadataValueMetadataValueItem> value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool, + System::Action> mixed + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + case IReadOnlyList value: + mixed(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of OnYesSetMetadataValue" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...}, + /// (IReadOnlyList<OnYesSetMetadataValueMetadataValueItem> value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool, + System::Func, T> mixed + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + IReadOnlyList value => mixed(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of OnYesSetMetadataValue" + ), + }; + } + + public static implicit operator OnYesSetMetadataValue(string value) => new(value); + + public static implicit operator OnYesSetMetadataValue(double value) => new(value); + + public static implicit operator OnYesSetMetadataValue(bool value) => new(value); + + public static implicit operator OnYesSetMetadataValue( + List value + ) => new((IReadOnlyList)value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of OnYesSetMetadataValue" + ); + } + this.Switch( + (_) => { }, + (_) => { }, + (_) => { }, + (mixed) => + { + foreach (var item in mixed) + { + item.Validate(); + } + } + ); + } + + public virtual bool Equals(OnYesSetMetadataValue? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + IReadOnlyList _ => 3, + _ => -1, + }; + } +} + +sealed class OnYesSetMetadataValueConverter : JsonConverter +{ + public override OnYesSetMetadataValue? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize< + List + >(element, options); + if (deserialized != null) + { + foreach (var item in deserialized) + { + item.Validate(); + } + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + OnYesSetMetadataValue value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(OnYesSetMetadataValueMetadataValueItemConverter))] +public record class OnYesSetMetadataValueMetadataValueItem : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public OnYesSetMetadataValueMetadataValueItem(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OnYesSetMetadataValueMetadataValueItem(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OnYesSetMetadataValueMetadataValueItem(bool value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public OnYesSetMetadataValueMetadataValueItem(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of OnYesSetMetadataValueMetadataValueItem" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of OnYesSetMetadataValueMetadataValueItem" + ), + }; + } + + public static implicit operator OnYesSetMetadataValueMetadataValueItem(string value) => + new(value); + + public static implicit operator OnYesSetMetadataValueMetadataValueItem(double value) => + new(value); + + public static implicit operator OnYesSetMetadataValueMetadataValueItem(bool value) => + new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of OnYesSetMetadataValueMetadataValueItem" + ); + } + } + + public virtual bool Equals(OnYesSetMetadataValueMetadataValueItem? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + _ => -1, + }; + } +} + +sealed class OnYesSetMetadataValueMetadataValueItemConverter + : JsonConverter +{ + public override OnYesSetMetadataValueMetadataValueItem? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + OnYesSetMetadataValueMetadataValueItem value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class OnYesUnsetMetadata : JsonModel +{ + /// + /// Name of the custom metadata field to remove. + /// + public required string Field + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("field"); + } + init { this._rawData.Set("field", value); } + } + + /// + public override void Validate() + { + _ = this.Field; + } + + public OnYesUnsetMetadata() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public OnYesUnsetMetadata(OnYesUnsetMetadata onYesUnsetMetadata) + : base(onYesUnsetMetadata) { } +#pragma warning restore CS8618 + + public OnYesUnsetMetadata(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + OnYesUnsetMetadata(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static OnYesUnsetMetadata FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public OnYesUnsetMetadata(string field) + : this() + { + this.Field = field; + } +} + +class OnYesUnsetMetadataFromRaw : IFromRawJson +{ + /// + public OnYesUnsetMetadata FromRawUnchecked(IReadOnlyDictionary rawData) => + OnYesUnsetMetadata.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/ExtensionItem.cs b/src/Imagekit/Models/ExtensionItem.cs new file mode 100644 index 00000000..587caa78 --- /dev/null +++ b/src/Imagekit/Models/ExtensionItem.cs @@ -0,0 +1,5495 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models; + +[JsonConverter(typeof(ExtensionItemConverter))] +public record class ExtensionItem : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public ExtensionItem(ExtensionItemRemoveBg value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public ExtensionItem(ExtensionItemAutoTaggingExtension value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public ExtensionItem(ExtensionItemAIAutoDescription value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public ExtensionItem(ExtensionItemAITasks value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public ExtensionItem(SavedExtension value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public ExtensionItem(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickRemoveBg(out var value)) { + /// // `value` is of type `ExtensionItemRemoveBg` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickRemoveBg([NotNullWhen(true)] out ExtensionItemRemoveBg? value) + { + value = this.Value as ExtensionItemRemoveBg; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickAutoTaggingExtension(out var value)) { + /// // `value` is of type `ExtensionItemAutoTaggingExtension` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickAutoTaggingExtension( + [NotNullWhen(true)] out ExtensionItemAutoTaggingExtension? value + ) + { + value = this.Value as ExtensionItemAutoTaggingExtension; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickAIAutoDescription(out var value)) { + /// // `value` is of type `ExtensionItemAIAutoDescription` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickAIAutoDescription( + [NotNullWhen(true)] out ExtensionItemAIAutoDescription? value + ) + { + value = this.Value as ExtensionItemAIAutoDescription; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickAITasks(out var value)) { + /// // `value` is of type `ExtensionItemAITasks` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickAITasks([NotNullWhen(true)] out ExtensionItemAITasks? value) + { + value = this.Value as ExtensionItemAITasks; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickSavedExtension(out var value)) { + /// // `value` is of type `SavedExtension` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickSavedExtension([NotNullWhen(true)] out SavedExtension? value) + { + value = this.Value as SavedExtension; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (ExtensionItemRemoveBg value) => {...}, + /// (ExtensionItemAutoTaggingExtension value) => {...}, + /// (ExtensionItemAIAutoDescription value) => {...}, + /// (ExtensionItemAITasks value) => {...}, + /// (SavedExtension value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action removeBg, + System::Action autoTaggingExtension, + System::Action aiAutoDescription, + System::Action aiTasks, + System::Action savedExtension + ) + { + switch (this.Value) + { + case ExtensionItemRemoveBg value: + removeBg(value); + break; + case ExtensionItemAutoTaggingExtension value: + autoTaggingExtension(value); + break; + case ExtensionItemAIAutoDescription value: + aiAutoDescription(value); + break; + case ExtensionItemAITasks value: + aiTasks(value); + break; + case SavedExtension value: + savedExtension(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItem" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (ExtensionItemRemoveBg value) => {...}, + /// (ExtensionItemAutoTaggingExtension value) => {...}, + /// (ExtensionItemAIAutoDescription value) => {...}, + /// (ExtensionItemAITasks value) => {...}, + /// (SavedExtension value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func removeBg, + System::Func autoTaggingExtension, + System::Func aiAutoDescription, + System::Func aiTasks, + System::Func savedExtension + ) + { + return this.Value switch + { + ExtensionItemRemoveBg value => removeBg(value), + ExtensionItemAutoTaggingExtension value => autoTaggingExtension(value), + ExtensionItemAIAutoDescription value => aiAutoDescription(value), + ExtensionItemAITasks value => aiTasks(value), + SavedExtension value => savedExtension(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItem" + ), + }; + } + + public static implicit operator ExtensionItem(ExtensionItemRemoveBg value) => new(value); + + public static implicit operator ExtensionItem(ExtensionItemAutoTaggingExtension value) => + new(value); + + public static implicit operator ExtensionItem(ExtensionItemAIAutoDescription value) => + new(value); + + public static implicit operator ExtensionItem(ExtensionItemAITasks value) => new(value); + + public static implicit operator ExtensionItem(SavedExtension value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItem" + ); + } + this.Switch( + (removeBg) => removeBg.Validate(), + (autoTaggingExtension) => autoTaggingExtension.Validate(), + (aiAutoDescription) => aiAutoDescription.Validate(), + (aiTasks) => aiTasks.Validate(), + (savedExtension) => savedExtension.Validate() + ); + } + + public virtual bool Equals(ExtensionItem? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + ExtensionItemRemoveBg _ => 0, + ExtensionItemAutoTaggingExtension _ => 1, + ExtensionItemAIAutoDescription _ => 2, + ExtensionItemAITasks _ => 3, + SavedExtension _ => 4, + _ => -1, + }; + } +} + +sealed class ExtensionItemConverter : JsonConverter +{ + public override ExtensionItem? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + string? name; + try + { + name = element.GetProperty("name").GetString(); + } + catch + { + name = null; + } + + switch (name) + { + case "remove-bg": + { + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "ai-auto-description": + { + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "ai-tasks": + { + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "saved-extension": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + default: + { + try + { + var deserialized = + JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + } + } + + public override void Write( + Utf8JsonWriter writer, + ExtensionItem value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class ExtensionItemRemoveBg : JsonModel +{ + /// + /// Specifies the background removal extension. + /// + public JsonElement Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("name"); + } + init { this._rawData.Set("name", value); } + } + + public ExtensionItemRemoveBgOptions? Options + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("options"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("options", value); + } + } + + /// + public override void Validate() + { + if (!JsonElement.DeepEquals(this.Name, JsonSerializer.SerializeToElement("remove-bg"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + this.Options?.Validate(); + } + + public ExtensionItemRemoveBg() + { + this.Name = JsonSerializer.SerializeToElement("remove-bg"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExtensionItemRemoveBg(ExtensionItemRemoveBg extensionItemRemoveBg) + : base(extensionItemRemoveBg) { } +#pragma warning restore CS8618 + + public ExtensionItemRemoveBg(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Name = JsonSerializer.SerializeToElement("remove-bg"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExtensionItemRemoveBg(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExtensionItemRemoveBg FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ExtensionItemRemoveBgFromRaw : IFromRawJson +{ + /// + public ExtensionItemRemoveBg FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ExtensionItemRemoveBg.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter) +)] +public sealed record class ExtensionItemRemoveBgOptions : JsonModel +{ + /// + /// Whether to add an artificial shadow to the result. Default is false. Note: + /// Adding shadows is currently only supported for car photos. + /// + public bool? AddShadow + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("add_shadow"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("add_shadow", value); + } + } + + /// + /// Specifies a solid color background using hex code (e.g., "81d4fa", "fff") + /// or color name (e.g., "green"). If this parameter is set, `bg_image_url` must + /// be empty. + /// + public string? BgColor + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("bg_color"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("bg_color", value); + } + } + + /// + /// Sets a background image from a URL. If this parameter is set, `bg_color` must + /// be empty. + /// + public string? BgImageUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("bg_image_url"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("bg_image_url", value); + } + } + + /// + /// Allows semi-transparent regions in the result. Default is true. Note: Semitransparency + /// is currently only supported for car windows. + /// + public bool? Semitransparency + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("semitransparency"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("semitransparency", value); + } + } + + /// + public override void Validate() + { + _ = this.AddShadow; + _ = this.BgColor; + _ = this.BgImageUrl; + _ = this.Semitransparency; + } + + public ExtensionItemRemoveBgOptions() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExtensionItemRemoveBgOptions(ExtensionItemRemoveBgOptions extensionItemRemoveBgOptions) + : base(extensionItemRemoveBgOptions) { } +#pragma warning restore CS8618 + + public ExtensionItemRemoveBgOptions(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExtensionItemRemoveBgOptions(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExtensionItemRemoveBgOptions FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ExtensionItemRemoveBgOptionsFromRaw : IFromRawJson +{ + /// + public ExtensionItemRemoveBgOptions FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ExtensionItemRemoveBgOptions.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter< + ExtensionItemAutoTaggingExtension, + ExtensionItemAutoTaggingExtensionFromRaw + >) +)] +public sealed record class ExtensionItemAutoTaggingExtension : JsonModel +{ + /// + /// Maximum number of tags to attach to the asset. + /// + public required long MaxTags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("maxTags"); + } + init { this._rawData.Set("maxTags", value); } + } + + /// + /// Minimum confidence level for tags to be considered valid. + /// + public required long MinConfidence + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("minConfidence"); + } + init { this._rawData.Set("minConfidence", value); } + } + + /// + /// Specifies the auto-tagging extension used. + /// + public required ApiEnum Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass< + ApiEnum + >("name"); + } + init { this._rawData.Set("name", value); } + } + + /// + public override void Validate() + { + _ = this.MaxTags; + _ = this.MinConfidence; + this.Name.Validate(); + } + + public ExtensionItemAutoTaggingExtension() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExtensionItemAutoTaggingExtension( + ExtensionItemAutoTaggingExtension extensionItemAutoTaggingExtension + ) + : base(extensionItemAutoTaggingExtension) { } +#pragma warning restore CS8618 + + public ExtensionItemAutoTaggingExtension(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExtensionItemAutoTaggingExtension(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExtensionItemAutoTaggingExtension FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ExtensionItemAutoTaggingExtensionFromRaw : IFromRawJson +{ + /// + public ExtensionItemAutoTaggingExtension FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ExtensionItemAutoTaggingExtension.FromRawUnchecked(rawData); +} + +/// +/// Specifies the auto-tagging extension used. +/// +[JsonConverter(typeof(ExtensionItemAutoTaggingExtensionNameConverter))] +public enum ExtensionItemAutoTaggingExtensionName +{ + GoogleAutoTagging, + AwsAutoTagging, +} + +sealed class ExtensionItemAutoTaggingExtensionNameConverter + : JsonConverter +{ + public override ExtensionItemAutoTaggingExtensionName Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "google-auto-tagging" => ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging, + "aws-auto-tagging" => ExtensionItemAutoTaggingExtensionName.AwsAutoTagging, + _ => (ExtensionItemAutoTaggingExtensionName)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + ExtensionItemAutoTaggingExtensionName value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + ExtensionItemAutoTaggingExtensionName.GoogleAutoTagging => "google-auto-tagging", + ExtensionItemAutoTaggingExtensionName.AwsAutoTagging => "aws-auto-tagging", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +[JsonConverter(typeof(ExtensionItemAIAutoDescriptionConverter))] +public record class ExtensionItemAIAutoDescription +{ + public JsonElement Element { get; private init; } + + public ExtensionItemAIAutoDescription() + { + Element = JsonSerializer.Deserialize( + """ + { + "name": "ai-auto-description" + } + """ + ); + } + + internal ExtensionItemAIAutoDescription(JsonElement element) + { + Element = element; + } + + /// + /// Validates that the instance's underlying value is the expected constant. + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public void Validate() + { + if (this != new ExtensionItemAIAutoDescription()) + { + throw new ImageKitInvalidDataException( + "Invalid value given for 'ExtensionItemAIAutoDescription'" + ); + } + } + + public override int GetHashCode() + { + return 0; + } + + public virtual bool Equals(ExtensionItemAIAutoDescription? other) + { + if (other == null) + { + return false; + } + + return JsonElement.DeepEquals(this.Element, other.Element); + } +} + +class ExtensionItemAIAutoDescriptionConverter : JsonConverter +{ + public override ExtensionItemAIAutoDescription? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return new(JsonSerializer.Deserialize(ref reader, options)); + } + + public override void Write( + Utf8JsonWriter writer, + ExtensionItemAIAutoDescription value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Element, options); + } +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class ExtensionItemAITasks : JsonModel +{ + /// + /// Specifies the AI tasks extension for automated image analysis using AI models. + /// + public JsonElement Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("name"); + } + init { this._rawData.Set("name", value); } + } + + /// + /// Array of task objects defining AI operations to perform on the asset. + /// + public required IReadOnlyList Tasks + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct>( + "tasks" + ); + } + init + { + this._rawData.Set>( + "tasks", + ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + if (!JsonElement.DeepEquals(this.Name, JsonSerializer.SerializeToElement("ai-tasks"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + foreach (var item in this.Tasks) + { + item.Validate(); + } + } + + public ExtensionItemAITasks() + { + this.Name = JsonSerializer.SerializeToElement("ai-tasks"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExtensionItemAITasks(ExtensionItemAITasks extensionItemAITasks) + : base(extensionItemAITasks) { } +#pragma warning restore CS8618 + + public ExtensionItemAITasks(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Name = JsonSerializer.SerializeToElement("ai-tasks"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExtensionItemAITasks(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExtensionItemAITasks FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public ExtensionItemAITasks(IReadOnlyList tasks) + : this() + { + this.Tasks = tasks; + } +} + +class ExtensionItemAITasksFromRaw : IFromRawJson +{ + /// + public ExtensionItemAITasks FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ExtensionItemAITasks.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(ExtensionItemAITasksTaskConverter))] +public record class ExtensionItemAITasksTask : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public string Instruction + { + get + { + return Match( + selectTags: (x) => x.Instruction, + selectMetadata: (x) => x.Instruction, + yesNo: (x) => x.Instruction + ); + } + } + + public JsonElement Type + { + get + { + return Match( + selectTags: (x) => x.Type, + selectMetadata: (x) => x.Type, + yesNo: (x) => x.Type + ); + } + } + + public long? MaxSelections + { + get + { + return Match( + selectTags: (x) => x.MaxSelections, + selectMetadata: (x) => x.MaxSelections, + yesNo: (_) => null + ); + } + } + + public long? MinSelections + { + get + { + return Match( + selectTags: (x) => x.MinSelections, + selectMetadata: (x) => x.MinSelections, + yesNo: (_) => null + ); + } + } + + public ExtensionItemAITasksTask( + ExtensionItemAITasksTaskSelectTags value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTask( + ExtensionItemAITasksTaskSelectMetadata value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTask( + ExtensionItemAITasksTaskYesNo value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTask(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickSelectTags(out var value)) { + /// // `value` is of type `ExtensionItemAITasksTaskSelectTags` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickSelectTags([NotNullWhen(true)] out ExtensionItemAITasksTaskSelectTags? value) + { + value = this.Value as ExtensionItemAITasksTaskSelectTags; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickSelectMetadata(out var value)) { + /// // `value` is of type `ExtensionItemAITasksTaskSelectMetadata` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickSelectMetadata( + [NotNullWhen(true)] out ExtensionItemAITasksTaskSelectMetadata? value + ) + { + value = this.Value as ExtensionItemAITasksTaskSelectMetadata; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickYesNo(out var value)) { + /// // `value` is of type `ExtensionItemAITasksTaskYesNo` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickYesNo([NotNullWhen(true)] out ExtensionItemAITasksTaskYesNo? value) + { + value = this.Value as ExtensionItemAITasksTaskYesNo; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (ExtensionItemAITasksTaskSelectTags value) => {...}, + /// (ExtensionItemAITasksTaskSelectMetadata value) => {...}, + /// (ExtensionItemAITasksTaskYesNo value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action selectTags, + System::Action selectMetadata, + System::Action yesNo + ) + { + switch (this.Value) + { + case ExtensionItemAITasksTaskSelectTags value: + selectTags(value); + break; + case ExtensionItemAITasksTaskSelectMetadata value: + selectMetadata(value); + break; + case ExtensionItemAITasksTaskYesNo value: + yesNo(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTask" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (ExtensionItemAITasksTaskSelectTags value) => {...}, + /// (ExtensionItemAITasksTaskSelectMetadata value) => {...}, + /// (ExtensionItemAITasksTaskYesNo value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func selectTags, + System::Func selectMetadata, + System::Func yesNo + ) + { + return this.Value switch + { + ExtensionItemAITasksTaskSelectTags value => selectTags(value), + ExtensionItemAITasksTaskSelectMetadata value => selectMetadata(value), + ExtensionItemAITasksTaskYesNo value => yesNo(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTask" + ), + }; + } + + public static implicit operator ExtensionItemAITasksTask( + ExtensionItemAITasksTaskSelectTags value + ) => new(value); + + public static implicit operator ExtensionItemAITasksTask( + ExtensionItemAITasksTaskSelectMetadata value + ) => new(value); + + public static implicit operator ExtensionItemAITasksTask(ExtensionItemAITasksTaskYesNo value) => + new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTask" + ); + } + this.Switch( + (selectTags) => selectTags.Validate(), + (selectMetadata) => selectMetadata.Validate(), + (yesNo) => yesNo.Validate() + ); + } + + public virtual bool Equals(ExtensionItemAITasksTask? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + ExtensionItemAITasksTaskSelectTags _ => 0, + ExtensionItemAITasksTaskSelectMetadata _ => 1, + ExtensionItemAITasksTaskYesNo _ => 2, + _ => -1, + }; + } +} + +sealed class ExtensionItemAITasksTaskConverter : JsonConverter +{ + public override ExtensionItemAITasksTask? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + string? type; + try + { + type = element.GetProperty("type").GetString(); + } + catch + { + type = null; + } + + switch (type) + { + case "select_tags": + { + try + { + var deserialized = + JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "select_metadata": + { + try + { + var deserialized = + JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "yes_no": + { + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + default: + { + return new ExtensionItemAITasksTask(element); + } + } + } + + public override void Write( + Utf8JsonWriter writer, + ExtensionItemAITasksTask value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter( + typeof(JsonModelConverter< + ExtensionItemAITasksTaskSelectTags, + ExtensionItemAITasksTaskSelectTagsFromRaw + >) +)] +public sealed record class ExtensionItemAITasksTaskSelectTags : JsonModel +{ + /// + /// The question or instruction for the AI to analyze the image. + /// + public required string Instruction + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("instruction"); + } + init { this._rawData.Set("instruction", value); } + } + + /// + /// Task type that analyzes the image and adds matching tags from a vocabulary. + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Maximum number of tags to select from the vocabulary. + /// + public long? MaxSelections + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("max_selections"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("max_selections", value); + } + } + + /// + /// Minimum number of tags to select from the vocabulary. + /// + public long? MinSelections + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("min_selections"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("min_selections", value); + } + } + + /// + /// Array of possible tag values. The combined length of all strings must not + /// exceed 500 characters, and values cannot include the `%` character. When + /// providing large vocabularies (more than 30 items), the AI may not follow + /// the list strictly. + /// + public IReadOnlyList? Vocabulary + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("vocabulary"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "vocabulary", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.Instruction; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("select_tags"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.MaxSelections; + _ = this.MinSelections; + _ = this.Vocabulary; + } + + public ExtensionItemAITasksTaskSelectTags() + { + this.Type = JsonSerializer.SerializeToElement("select_tags"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExtensionItemAITasksTaskSelectTags( + ExtensionItemAITasksTaskSelectTags extensionItemAITasksTaskSelectTags + ) + : base(extensionItemAITasksTaskSelectTags) { } +#pragma warning restore CS8618 + + public ExtensionItemAITasksTaskSelectTags(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("select_tags"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExtensionItemAITasksTaskSelectTags(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExtensionItemAITasksTaskSelectTags FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public ExtensionItemAITasksTaskSelectTags(string instruction) + : this() + { + this.Instruction = instruction; + } +} + +class ExtensionItemAITasksTaskSelectTagsFromRaw : IFromRawJson +{ + /// + public ExtensionItemAITasksTaskSelectTags FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ExtensionItemAITasksTaskSelectTags.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter< + ExtensionItemAITasksTaskSelectMetadata, + ExtensionItemAITasksTaskSelectMetadataFromRaw + >) +)] +public sealed record class ExtensionItemAITasksTaskSelectMetadata : JsonModel +{ + /// + /// Name of the custom metadata field to set. The field must exist in your account. + /// + public required string Field + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("field"); + } + init { this._rawData.Set("field", value); } + } + + /// + /// The question or instruction for the AI to analyze the image. + /// + public required string Instruction + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("instruction"); + } + init { this._rawData.Set("instruction", value); } + } + + /// + /// Task type that analyzes the image and sets a custom metadata field value from + /// a vocabulary. + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Maximum number of values to select from the vocabulary. + /// + public long? MaxSelections + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("max_selections"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("max_selections", value); + } + } + + /// + /// Minimum number of values to select from the vocabulary. + /// + public long? MinSelections + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("min_selections"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("min_selections", value); + } + } + + /// + /// An array of possible values matching the custom metadata field type. If not + /// provided for SingleSelect or MultiSelect field types, all values from the + /// custom metadata field definition will be used. When providing large vocabularies + /// (above 30 items), the AI may not strictly adhere to the list. + /// + public IReadOnlyList? Vocabulary + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct< + ImmutableArray + >("vocabulary"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "vocabulary", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.Field; + _ = this.Instruction; + if ( + !JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("select_metadata")) + ) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.MaxSelections; + _ = this.MinSelections; + foreach (var item in this.Vocabulary ?? []) + { + item.Validate(); + } + } + + public ExtensionItemAITasksTaskSelectMetadata() + { + this.Type = JsonSerializer.SerializeToElement("select_metadata"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExtensionItemAITasksTaskSelectMetadata( + ExtensionItemAITasksTaskSelectMetadata extensionItemAITasksTaskSelectMetadata + ) + : base(extensionItemAITasksTaskSelectMetadata) { } +#pragma warning restore CS8618 + + public ExtensionItemAITasksTaskSelectMetadata(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("select_metadata"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExtensionItemAITasksTaskSelectMetadata(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExtensionItemAITasksTaskSelectMetadata FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ExtensionItemAITasksTaskSelectMetadataFromRaw + : IFromRawJson +{ + /// + public ExtensionItemAITasksTaskSelectMetadata FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ExtensionItemAITasksTaskSelectMetadata.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(ExtensionItemAITasksTaskSelectMetadataVocabularyConverter))] +public record class ExtensionItemAITasksTaskSelectMetadataVocabulary : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public ExtensionItemAITasksTaskSelectMetadataVocabulary( + string value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTaskSelectMetadataVocabulary( + double value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTaskSelectMetadataVocabulary(bool value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTaskSelectMetadataVocabulary(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTaskSelectMetadataVocabulary" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTaskSelectMetadataVocabulary" + ), + }; + } + + public static implicit operator ExtensionItemAITasksTaskSelectMetadataVocabulary( + string value + ) => new(value); + + public static implicit operator ExtensionItemAITasksTaskSelectMetadataVocabulary( + double value + ) => new(value); + + public static implicit operator ExtensionItemAITasksTaskSelectMetadataVocabulary(bool value) => + new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTaskSelectMetadataVocabulary" + ); + } + } + + public virtual bool Equals(ExtensionItemAITasksTaskSelectMetadataVocabulary? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + _ => -1, + }; + } +} + +sealed class ExtensionItemAITasksTaskSelectMetadataVocabularyConverter + : JsonConverter +{ + public override ExtensionItemAITasksTaskSelectMetadataVocabulary? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + ExtensionItemAITasksTaskSelectMetadataVocabulary value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter( + typeof(JsonModelConverter) +)] +public sealed record class ExtensionItemAITasksTaskYesNo : JsonModel +{ + /// + /// The yes/no question for the AI to answer about the image. + /// + public required string Instruction + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("instruction"); + } + init { this._rawData.Set("instruction", value); } + } + + /// + /// Task type that asks a yes/no question and executes actions based on the answer. + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Actions to execute if the AI answers no. + /// + public ExtensionItemAITasksTaskYesNoOnNo? OnNo + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("on_no"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("on_no", value); + } + } + + /// + /// Actions to execute if the AI cannot determine the answer. + /// + public ExtensionItemAITasksTaskYesNoOnUnknown? OnUnknown + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass( + "on_unknown" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("on_unknown", value); + } + } + + /// + /// Actions to execute if the AI answers yes. + /// + public ExtensionItemAITasksTaskYesNoOnYes? OnYes + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("on_yes"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("on_yes", value); + } + } + + /// + public override void Validate() + { + _ = this.Instruction; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("yes_no"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + this.OnNo?.Validate(); + this.OnUnknown?.Validate(); + this.OnYes?.Validate(); + } + + public ExtensionItemAITasksTaskYesNo() + { + this.Type = JsonSerializer.SerializeToElement("yes_no"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExtensionItemAITasksTaskYesNo( + ExtensionItemAITasksTaskYesNo extensionItemAITasksTaskYesNo + ) + : base(extensionItemAITasksTaskYesNo) { } +#pragma warning restore CS8618 + + public ExtensionItemAITasksTaskYesNo(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("yes_no"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExtensionItemAITasksTaskYesNo(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExtensionItemAITasksTaskYesNo FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public ExtensionItemAITasksTaskYesNo(string instruction) + : this() + { + this.Instruction = instruction; + } +} + +class ExtensionItemAITasksTaskYesNoFromRaw : IFromRawJson +{ + /// + public ExtensionItemAITasksTaskYesNo FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ExtensionItemAITasksTaskYesNo.FromRawUnchecked(rawData); +} + +/// +/// Actions to execute if the AI answers no. +/// +[JsonConverter( + typeof(JsonModelConverter< + ExtensionItemAITasksTaskYesNoOnNo, + ExtensionItemAITasksTaskYesNoOnNoFromRaw + >) +)] +public sealed record class ExtensionItemAITasksTaskYesNoOnNo : JsonModel +{ + /// + /// Array of tag strings to add to the asset. + /// + public IReadOnlyList? AddTags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("add_tags"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "add_tags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Array of tag strings to remove from the asset. + /// + public IReadOnlyList? RemoveTags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("remove_tags"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "remove_tags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Array of custom metadata field updates. + /// + public IReadOnlyList? SetMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct< + ImmutableArray + >("set_metadata"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "set_metadata", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Array of custom metadata fields to remove. + /// + public IReadOnlyList? UnsetMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct< + ImmutableArray + >("unset_metadata"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "unset_metadata", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.AddTags; + _ = this.RemoveTags; + foreach (var item in this.SetMetadata ?? []) + { + item.Validate(); + } + foreach (var item in this.UnsetMetadata ?? []) + { + item.Validate(); + } + } + + public ExtensionItemAITasksTaskYesNoOnNo() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExtensionItemAITasksTaskYesNoOnNo( + ExtensionItemAITasksTaskYesNoOnNo extensionItemAITasksTaskYesNoOnNo + ) + : base(extensionItemAITasksTaskYesNoOnNo) { } +#pragma warning restore CS8618 + + public ExtensionItemAITasksTaskYesNoOnNo(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExtensionItemAITasksTaskYesNoOnNo(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExtensionItemAITasksTaskYesNoOnNo FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ExtensionItemAITasksTaskYesNoOnNoFromRaw : IFromRawJson +{ + /// + public ExtensionItemAITasksTaskYesNoOnNo FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ExtensionItemAITasksTaskYesNoOnNo.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter< + ExtensionItemAITasksTaskYesNoOnNoSetMetadata, + ExtensionItemAITasksTaskYesNoOnNoSetMetadataFromRaw + >) +)] +public sealed record class ExtensionItemAITasksTaskYesNoOnNoSetMetadata : JsonModel +{ + /// + /// Name of the custom metadata field to set. + /// + public required string Field + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("field"); + } + init { this._rawData.Set("field", value); } + } + + /// + /// Value to set for the custom metadata field. The value type should match the + /// custom metadata field type. + /// + public required ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue Value + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "value" + ); + } + init { this._rawData.Set("value", value); } + } + + /// + public override void Validate() + { + _ = this.Field; + this.Value.Validate(); + } + + public ExtensionItemAITasksTaskYesNoOnNoSetMetadata() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExtensionItemAITasksTaskYesNoOnNoSetMetadata( + ExtensionItemAITasksTaskYesNoOnNoSetMetadata extensionItemAITasksTaskYesNoOnNoSetMetadata + ) + : base(extensionItemAITasksTaskYesNoOnNoSetMetadata) { } +#pragma warning restore CS8618 + + public ExtensionItemAITasksTaskYesNoOnNoSetMetadata( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExtensionItemAITasksTaskYesNoOnNoSetMetadata(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExtensionItemAITasksTaskYesNoOnNoSetMetadata FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ExtensionItemAITasksTaskYesNoOnNoSetMetadataFromRaw + : IFromRawJson +{ + /// + public ExtensionItemAITasksTaskYesNoOnNoSetMetadata FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ExtensionItemAITasksTaskYesNoOnNoSetMetadata.FromRawUnchecked(rawData); +} + +/// +/// Value to set for the custom metadata field. The value type should match the custom +/// metadata field type. +/// +[JsonConverter(typeof(ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueConverter))] +public record class ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue( + string value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue( + double value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue( + bool value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue( + IReadOnlyList value, + JsonElement? element = null + ) + { + this.Value = ImmutableArray.ToImmutableArray(value); + this._element = element; + } + + public ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type where T is a ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem. + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickMixed(out var value)) { + /// // `value` is of type `IReadOnlyList<ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem>` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickMixed( + [NotNullWhen(true)] + out IReadOnlyList? value + ) + { + value = + this.Value + as IReadOnlyList; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...}, + /// (IReadOnlyList<ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem> value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool, + System::Action< + IReadOnlyList + > mixed + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + case IReadOnlyList value: + mixed(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...}, + /// (IReadOnlyList<ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem> value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool, + System::Func< + IReadOnlyList, + T + > mixed + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + IReadOnlyList value => + mixed(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue" + ), + }; + } + + public static implicit operator ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue( + string value + ) => new(value); + + public static implicit operator ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue( + double value + ) => new(value); + + public static implicit operator ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue(bool value) => + new(value); + + public static implicit operator ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue( + List value + ) => + new( + (IReadOnlyList)value + ); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue" + ); + } + this.Switch( + (_) => { }, + (_) => { }, + (_) => { }, + (mixed) => + { + foreach (var item in mixed) + { + item.Validate(); + } + } + ); + } + + public virtual bool Equals(ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + IReadOnlyList _ => + 3, + _ => -1, + }; + } +} + +sealed class ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueConverter + : JsonConverter +{ + public override ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize< + List + >(element, options); + if (deserialized != null) + { + foreach (var item in deserialized) + { + item.Validate(); + } + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + ExtensionItemAITasksTaskYesNoOnNoSetMetadataValue value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItemConverter))] +public record class ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem( + string value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem( + double value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem( + bool value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem" + ), + }; + } + + public static implicit operator ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem( + string value + ) => new(value); + + public static implicit operator ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem( + double value + ) => new(value); + + public static implicit operator ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem( + bool value + ) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem" + ); + } + } + + public virtual bool Equals( + ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem? other + ) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + _ => -1, + }; + } +} + +sealed class ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItemConverter + : JsonConverter +{ + public override ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + ExtensionItemAITasksTaskYesNoOnNoSetMetadataValueMetadataValueItem value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter( + typeof(JsonModelConverter< + ExtensionItemAITasksTaskYesNoOnNoUnsetMetadata, + ExtensionItemAITasksTaskYesNoOnNoUnsetMetadataFromRaw + >) +)] +public sealed record class ExtensionItemAITasksTaskYesNoOnNoUnsetMetadata : JsonModel +{ + /// + /// Name of the custom metadata field to remove. + /// + public required string Field + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("field"); + } + init { this._rawData.Set("field", value); } + } + + /// + public override void Validate() + { + _ = this.Field; + } + + public ExtensionItemAITasksTaskYesNoOnNoUnsetMetadata() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExtensionItemAITasksTaskYesNoOnNoUnsetMetadata( + ExtensionItemAITasksTaskYesNoOnNoUnsetMetadata extensionItemAITasksTaskYesNoOnNoUnsetMetadata + ) + : base(extensionItemAITasksTaskYesNoOnNoUnsetMetadata) { } +#pragma warning restore CS8618 + + public ExtensionItemAITasksTaskYesNoOnNoUnsetMetadata( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExtensionItemAITasksTaskYesNoOnNoUnsetMetadata(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExtensionItemAITasksTaskYesNoOnNoUnsetMetadata FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public ExtensionItemAITasksTaskYesNoOnNoUnsetMetadata(string field) + : this() + { + this.Field = field; + } +} + +class ExtensionItemAITasksTaskYesNoOnNoUnsetMetadataFromRaw + : IFromRawJson +{ + /// + public ExtensionItemAITasksTaskYesNoOnNoUnsetMetadata FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ExtensionItemAITasksTaskYesNoOnNoUnsetMetadata.FromRawUnchecked(rawData); +} + +/// +/// Actions to execute if the AI cannot determine the answer. +/// +[JsonConverter( + typeof(JsonModelConverter< + ExtensionItemAITasksTaskYesNoOnUnknown, + ExtensionItemAITasksTaskYesNoOnUnknownFromRaw + >) +)] +public sealed record class ExtensionItemAITasksTaskYesNoOnUnknown : JsonModel +{ + /// + /// Array of tag strings to add to the asset. + /// + public IReadOnlyList? AddTags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("add_tags"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "add_tags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Array of tag strings to remove from the asset. + /// + public IReadOnlyList? RemoveTags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("remove_tags"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "remove_tags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Array of custom metadata field updates. + /// + public IReadOnlyList? SetMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct< + ImmutableArray + >("set_metadata"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "set_metadata", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Array of custom metadata fields to remove. + /// + public IReadOnlyList? UnsetMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct< + ImmutableArray + >("unset_metadata"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "unset_metadata", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.AddTags; + _ = this.RemoveTags; + foreach (var item in this.SetMetadata ?? []) + { + item.Validate(); + } + foreach (var item in this.UnsetMetadata ?? []) + { + item.Validate(); + } + } + + public ExtensionItemAITasksTaskYesNoOnUnknown() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExtensionItemAITasksTaskYesNoOnUnknown( + ExtensionItemAITasksTaskYesNoOnUnknown extensionItemAITasksTaskYesNoOnUnknown + ) + : base(extensionItemAITasksTaskYesNoOnUnknown) { } +#pragma warning restore CS8618 + + public ExtensionItemAITasksTaskYesNoOnUnknown(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExtensionItemAITasksTaskYesNoOnUnknown(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExtensionItemAITasksTaskYesNoOnUnknown FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ExtensionItemAITasksTaskYesNoOnUnknownFromRaw + : IFromRawJson +{ + /// + public ExtensionItemAITasksTaskYesNoOnUnknown FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ExtensionItemAITasksTaskYesNoOnUnknown.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter< + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadata, + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataFromRaw + >) +)] +public sealed record class ExtensionItemAITasksTaskYesNoOnUnknownSetMetadata : JsonModel +{ + /// + /// Name of the custom metadata field to set. + /// + public required string Field + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("field"); + } + init { this._rawData.Set("field", value); } + } + + /// + /// Value to set for the custom metadata field. The value type should match the + /// custom metadata field type. + /// + public required ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue Value + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "value" + ); + } + init { this._rawData.Set("value", value); } + } + + /// + public override void Validate() + { + _ = this.Field; + this.Value.Validate(); + } + + public ExtensionItemAITasksTaskYesNoOnUnknownSetMetadata() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExtensionItemAITasksTaskYesNoOnUnknownSetMetadata( + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadata extensionItemAITasksTaskYesNoOnUnknownSetMetadata + ) + : base(extensionItemAITasksTaskYesNoOnUnknownSetMetadata) { } +#pragma warning restore CS8618 + + public ExtensionItemAITasksTaskYesNoOnUnknownSetMetadata( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadata(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExtensionItemAITasksTaskYesNoOnUnknownSetMetadata FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataFromRaw + : IFromRawJson +{ + /// + public ExtensionItemAITasksTaskYesNoOnUnknownSetMetadata FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ExtensionItemAITasksTaskYesNoOnUnknownSetMetadata.FromRawUnchecked(rawData); +} + +/// +/// Value to set for the custom metadata field. The value type should match the custom +/// metadata field type. +/// +[JsonConverter(typeof(ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueConverter))] +public record class ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue( + string value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue( + double value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue( + bool value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue( + IReadOnlyList value, + JsonElement? element = null + ) + { + this.Value = ImmutableArray.ToImmutableArray(value); + this._element = element; + } + + public ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type where T is a ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem. + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickMixed(out var value)) { + /// // `value` is of type `IReadOnlyList<ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem>` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickMixed( + [NotNullWhen(true)] + out IReadOnlyList? value + ) + { + value = + this.Value + as IReadOnlyList; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...}, + /// (IReadOnlyList<ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem> value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool, + System::Action< + IReadOnlyList + > mixed + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + case IReadOnlyList value: + mixed(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...}, + /// (IReadOnlyList<ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem> value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool, + System::Func< + IReadOnlyList, + T + > mixed + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + IReadOnlyList value => + mixed(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue" + ), + }; + } + + public static implicit operator ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue( + string value + ) => new(value); + + public static implicit operator ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue( + double value + ) => new(value); + + public static implicit operator ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue( + bool value + ) => new(value); + + public static implicit operator ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue( + List value + ) => + new( + (IReadOnlyList) + value + ); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue" + ); + } + this.Switch( + (_) => { }, + (_) => { }, + (_) => { }, + (mixed) => + { + foreach (var item in mixed) + { + item.Validate(); + } + } + ); + } + + public virtual bool Equals(ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + IReadOnlyList _ => + 3, + _ => -1, + }; + } +} + +sealed class ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueConverter + : JsonConverter +{ + public override ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize< + List + >(element, options); + if (deserialized != null) + { + foreach (var item in deserialized) + { + item.Validate(); + } + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValue value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter( + typeof(ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItemConverter) +)] +public record class ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem + : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem( + string value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem( + double value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem( + bool value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem( + JsonElement element + ) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem" + ), + }; + } + + public static implicit operator ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem( + string value + ) => new(value); + + public static implicit operator ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem( + double value + ) => new(value); + + public static implicit operator ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem( + bool value + ) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem" + ); + } + } + + public virtual bool Equals( + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem? other + ) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + _ => -1, + }; + } +} + +sealed class ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItemConverter + : JsonConverter +{ + public override ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + ExtensionItemAITasksTaskYesNoOnUnknownSetMetadataValueMetadataValueItem value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter( + typeof(JsonModelConverter< + ExtensionItemAITasksTaskYesNoOnUnknownUnsetMetadata, + ExtensionItemAITasksTaskYesNoOnUnknownUnsetMetadataFromRaw + >) +)] +public sealed record class ExtensionItemAITasksTaskYesNoOnUnknownUnsetMetadata : JsonModel +{ + /// + /// Name of the custom metadata field to remove. + /// + public required string Field + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("field"); + } + init { this._rawData.Set("field", value); } + } + + /// + public override void Validate() + { + _ = this.Field; + } + + public ExtensionItemAITasksTaskYesNoOnUnknownUnsetMetadata() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExtensionItemAITasksTaskYesNoOnUnknownUnsetMetadata( + ExtensionItemAITasksTaskYesNoOnUnknownUnsetMetadata extensionItemAITasksTaskYesNoOnUnknownUnsetMetadata + ) + : base(extensionItemAITasksTaskYesNoOnUnknownUnsetMetadata) { } +#pragma warning restore CS8618 + + public ExtensionItemAITasksTaskYesNoOnUnknownUnsetMetadata( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExtensionItemAITasksTaskYesNoOnUnknownUnsetMetadata( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExtensionItemAITasksTaskYesNoOnUnknownUnsetMetadata FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public ExtensionItemAITasksTaskYesNoOnUnknownUnsetMetadata(string field) + : this() + { + this.Field = field; + } +} + +class ExtensionItemAITasksTaskYesNoOnUnknownUnsetMetadataFromRaw + : IFromRawJson +{ + /// + public ExtensionItemAITasksTaskYesNoOnUnknownUnsetMetadata FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ExtensionItemAITasksTaskYesNoOnUnknownUnsetMetadata.FromRawUnchecked(rawData); +} + +/// +/// Actions to execute if the AI answers yes. +/// +[JsonConverter( + typeof(JsonModelConverter< + ExtensionItemAITasksTaskYesNoOnYes, + ExtensionItemAITasksTaskYesNoOnYesFromRaw + >) +)] +public sealed record class ExtensionItemAITasksTaskYesNoOnYes : JsonModel +{ + /// + /// Array of tag strings to add to the asset. + /// + public IReadOnlyList? AddTags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("add_tags"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "add_tags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Array of tag strings to remove from the asset. + /// + public IReadOnlyList? RemoveTags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("remove_tags"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "remove_tags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Array of custom metadata field updates. + /// + public IReadOnlyList? SetMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct< + ImmutableArray + >("set_metadata"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "set_metadata", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Array of custom metadata fields to remove. + /// + public IReadOnlyList? UnsetMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct< + ImmutableArray + >("unset_metadata"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "unset_metadata", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.AddTags; + _ = this.RemoveTags; + foreach (var item in this.SetMetadata ?? []) + { + item.Validate(); + } + foreach (var item in this.UnsetMetadata ?? []) + { + item.Validate(); + } + } + + public ExtensionItemAITasksTaskYesNoOnYes() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExtensionItemAITasksTaskYesNoOnYes( + ExtensionItemAITasksTaskYesNoOnYes extensionItemAITasksTaskYesNoOnYes + ) + : base(extensionItemAITasksTaskYesNoOnYes) { } +#pragma warning restore CS8618 + + public ExtensionItemAITasksTaskYesNoOnYes(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExtensionItemAITasksTaskYesNoOnYes(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExtensionItemAITasksTaskYesNoOnYes FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ExtensionItemAITasksTaskYesNoOnYesFromRaw : IFromRawJson +{ + /// + public ExtensionItemAITasksTaskYesNoOnYes FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ExtensionItemAITasksTaskYesNoOnYes.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter< + ExtensionItemAITasksTaskYesNoOnYesSetMetadata, + ExtensionItemAITasksTaskYesNoOnYesSetMetadataFromRaw + >) +)] +public sealed record class ExtensionItemAITasksTaskYesNoOnYesSetMetadata : JsonModel +{ + /// + /// Name of the custom metadata field to set. + /// + public required string Field + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("field"); + } + init { this._rawData.Set("field", value); } + } + + /// + /// Value to set for the custom metadata field. The value type should match the + /// custom metadata field type. + /// + public required ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue Value + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "value" + ); + } + init { this._rawData.Set("value", value); } + } + + /// + public override void Validate() + { + _ = this.Field; + this.Value.Validate(); + } + + public ExtensionItemAITasksTaskYesNoOnYesSetMetadata() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExtensionItemAITasksTaskYesNoOnYesSetMetadata( + ExtensionItemAITasksTaskYesNoOnYesSetMetadata extensionItemAITasksTaskYesNoOnYesSetMetadata + ) + : base(extensionItemAITasksTaskYesNoOnYesSetMetadata) { } +#pragma warning restore CS8618 + + public ExtensionItemAITasksTaskYesNoOnYesSetMetadata( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExtensionItemAITasksTaskYesNoOnYesSetMetadata(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExtensionItemAITasksTaskYesNoOnYesSetMetadata FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ExtensionItemAITasksTaskYesNoOnYesSetMetadataFromRaw + : IFromRawJson +{ + /// + public ExtensionItemAITasksTaskYesNoOnYesSetMetadata FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ExtensionItemAITasksTaskYesNoOnYesSetMetadata.FromRawUnchecked(rawData); +} + +/// +/// Value to set for the custom metadata field. The value type should match the custom +/// metadata field type. +/// +[JsonConverter(typeof(ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueConverter))] +public record class ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue( + string value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue( + double value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue( + bool value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue( + IReadOnlyList value, + JsonElement? element = null + ) + { + this.Value = ImmutableArray.ToImmutableArray(value); + this._element = element; + } + + public ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type where T is a ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem. + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickMixed(out var value)) { + /// // `value` is of type `IReadOnlyList<ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem>` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickMixed( + [NotNullWhen(true)] + out IReadOnlyList? value + ) + { + value = + this.Value + as IReadOnlyList; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...}, + /// (IReadOnlyList<ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem> value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool, + System::Action< + IReadOnlyList + > mixed + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + case IReadOnlyList value: + mixed(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...}, + /// (IReadOnlyList<ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem> value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool, + System::Func< + IReadOnlyList, + T + > mixed + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + IReadOnlyList value => + mixed(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue" + ), + }; + } + + public static implicit operator ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue( + string value + ) => new(value); + + public static implicit operator ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue( + double value + ) => new(value); + + public static implicit operator ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue( + bool value + ) => new(value); + + public static implicit operator ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue( + List value + ) => + new( + (IReadOnlyList) + value + ); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue" + ); + } + this.Switch( + (_) => { }, + (_) => { }, + (_) => { }, + (mixed) => + { + foreach (var item in mixed) + { + item.Validate(); + } + } + ); + } + + public virtual bool Equals(ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + IReadOnlyList _ => + 3, + _ => -1, + }; + } +} + +sealed class ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueConverter + : JsonConverter +{ + public override ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize< + List + >(element, options); + if (deserialized != null) + { + foreach (var item in deserialized) + { + item.Validate(); + } + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + ExtensionItemAITasksTaskYesNoOnYesSetMetadataValue value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter( + typeof(ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItemConverter) +)] +public record class ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem( + string value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem( + double value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem( + bool value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem" + ), + }; + } + + public static implicit operator ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem( + string value + ) => new(value); + + public static implicit operator ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem( + double value + ) => new(value); + + public static implicit operator ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem( + bool value + ) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem" + ); + } + } + + public virtual bool Equals( + ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem? other + ) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + _ => -1, + }; + } +} + +sealed class ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItemConverter + : JsonConverter +{ + public override ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + ExtensionItemAITasksTaskYesNoOnYesSetMetadataValueMetadataValueItem value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter( + typeof(JsonModelConverter< + ExtensionItemAITasksTaskYesNoOnYesUnsetMetadata, + ExtensionItemAITasksTaskYesNoOnYesUnsetMetadataFromRaw + >) +)] +public sealed record class ExtensionItemAITasksTaskYesNoOnYesUnsetMetadata : JsonModel +{ + /// + /// Name of the custom metadata field to remove. + /// + public required string Field + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("field"); + } + init { this._rawData.Set("field", value); } + } + + /// + public override void Validate() + { + _ = this.Field; + } + + public ExtensionItemAITasksTaskYesNoOnYesUnsetMetadata() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExtensionItemAITasksTaskYesNoOnYesUnsetMetadata( + ExtensionItemAITasksTaskYesNoOnYesUnsetMetadata extensionItemAITasksTaskYesNoOnYesUnsetMetadata + ) + : base(extensionItemAITasksTaskYesNoOnYesUnsetMetadata) { } +#pragma warning restore CS8618 + + public ExtensionItemAITasksTaskYesNoOnYesUnsetMetadata( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExtensionItemAITasksTaskYesNoOnYesUnsetMetadata(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExtensionItemAITasksTaskYesNoOnYesUnsetMetadata FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public ExtensionItemAITasksTaskYesNoOnYesUnsetMetadata(string field) + : this() + { + this.Field = field; + } +} + +class ExtensionItemAITasksTaskYesNoOnYesUnsetMetadataFromRaw + : IFromRawJson +{ + /// + public ExtensionItemAITasksTaskYesNoOnYesUnsetMetadata FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ExtensionItemAITasksTaskYesNoOnYesUnsetMetadata.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class SavedExtension : JsonModel +{ + /// + /// The unique ID of the saved extension to apply. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + /// + /// Indicates this is a reference to a saved extension. + /// + public JsonElement Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("name"); + } + init { this._rawData.Set("name", value); } + } + + /// + public override void Validate() + { + _ = this.ID; + if ( + !JsonElement.DeepEquals(this.Name, JsonSerializer.SerializeToElement("saved-extension")) + ) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + } + + public SavedExtension() + { + this.Name = JsonSerializer.SerializeToElement("saved-extension"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SavedExtension(SavedExtension savedExtension) + : base(savedExtension) { } +#pragma warning restore CS8618 + + public SavedExtension(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Name = JsonSerializer.SerializeToElement("saved-extension"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SavedExtension(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SavedExtension FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public SavedExtension(string id) + : this() + { + this.ID = id; + } +} + +class SavedExtensionFromRaw : IFromRawJson +{ + /// + public SavedExtension FromRawUnchecked(IReadOnlyDictionary rawData) => + SavedExtension.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Files/Bulk/BulkAddTagsParams.cs b/src/Imagekit/Models/Files/Bulk/BulkAddTagsParams.cs new file mode 100644 index 00000000..34861340 --- /dev/null +++ b/src/Imagekit/Models/Files/Bulk/BulkAddTagsParams.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; +using Text = System.Text; + +namespace Imagekit.Models.Files.Bulk; + +/// +/// This API adds tags to multiple files in bulk. A maximum of 50 files can be specified +/// at a time. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class BulkAddTagsParams : ParamsBase +{ + readonly JsonDictionary _rawBodyData = new(); + public IReadOnlyDictionary RawBodyData + { + get { return this._rawBodyData.Freeze(); } + } + + /// + /// An array of fileIds to which you want to add tags. + /// + public required IReadOnlyList FileIds + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullStruct>("fileIds"); + } + init + { + this._rawBodyData.Set>( + "fileIds", + ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// An array of tags that you want to add to the files. + /// + public required IReadOnlyList Tags + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullStruct>("tags"); + } + init + { + this._rawBodyData.Set>( + "tags", + ImmutableArray.ToImmutableArray(value) + ); + } + } + + public BulkAddTagsParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public BulkAddTagsParams(BulkAddTagsParams bulkAddTagsParams) + : base(bulkAddTagsParams) + { + this._rawBodyData = new(bulkAddTagsParams._rawBodyData); + } +#pragma warning restore CS8618 + + public BulkAddTagsParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + BulkAddTagsParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + FrozenDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } +#pragma warning restore CS8618 + + /// + public static BulkAddTagsParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + FrozenDictionary.ToFrozenDictionary(rawBodyData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this._rawBodyData.Freeze()), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(BulkAddTagsParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this._rawBodyData.Equals(other._rawBodyData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder(options.BaseUrl.ToString().TrimEnd('/') + "/v1/files/addTags") + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Files/Bulk/BulkAddTagsResponse.cs b/src/Imagekit/Models/Files/Bulk/BulkAddTagsResponse.cs new file mode 100644 index 00000000..8d60fee3 --- /dev/null +++ b/src/Imagekit/Models/Files/Bulk/BulkAddTagsResponse.cs @@ -0,0 +1,81 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models.Files.Bulk; + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class BulkAddTagsResponse : JsonModel +{ + /// + /// An array of fileIds that in which tags were successfully added. + /// + public IReadOnlyList? SuccessfullyUpdatedFileIds + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>( + "successfullyUpdatedFileIds" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "successfullyUpdatedFileIds", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.SuccessfullyUpdatedFileIds; + } + + public BulkAddTagsResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public BulkAddTagsResponse(BulkAddTagsResponse bulkAddTagsResponse) + : base(bulkAddTagsResponse) { } +#pragma warning restore CS8618 + + public BulkAddTagsResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + BulkAddTagsResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static BulkAddTagsResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class BulkAddTagsResponseFromRaw : IFromRawJson +{ + /// + public BulkAddTagsResponse FromRawUnchecked(IReadOnlyDictionary rawData) => + BulkAddTagsResponse.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Files/Bulk/BulkDeleteParams.cs b/src/Imagekit/Models/Files/Bulk/BulkDeleteParams.cs new file mode 100644 index 00000000..e63874c6 --- /dev/null +++ b/src/Imagekit/Models/Files/Bulk/BulkDeleteParams.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; +using Text = System.Text; + +namespace Imagekit.Models.Files.Bulk; + +/// +/// This API deletes multiple files and all their file versions permanently. +/// +/// Note: If a file or specific transformation has been requested in the past, +/// then the response is cached. Deleting a file does not purge the cache. You can +/// purge the cache using purge cache API. +/// +/// A maximum of 100 files can be deleted at a time. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class BulkDeleteParams : ParamsBase +{ + readonly JsonDictionary _rawBodyData = new(); + public IReadOnlyDictionary RawBodyData + { + get { return this._rawBodyData.Freeze(); } + } + + /// + /// An array of fileIds which you want to delete. + /// + public required IReadOnlyList FileIds + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullStruct>("fileIds"); + } + init + { + this._rawBodyData.Set>( + "fileIds", + ImmutableArray.ToImmutableArray(value) + ); + } + } + + public BulkDeleteParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public BulkDeleteParams(BulkDeleteParams bulkDeleteParams) + : base(bulkDeleteParams) + { + this._rawBodyData = new(bulkDeleteParams._rawBodyData); + } +#pragma warning restore CS8618 + + public BulkDeleteParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + BulkDeleteParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + FrozenDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } +#pragma warning restore CS8618 + + /// + public static BulkDeleteParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + FrozenDictionary.ToFrozenDictionary(rawBodyData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this._rawBodyData.Freeze()), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(BulkDeleteParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this._rawBodyData.Equals(other._rawBodyData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + "/v1/files/batch/deleteByFileIds" + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Files/Bulk/BulkDeleteResponse.cs b/src/Imagekit/Models/Files/Bulk/BulkDeleteResponse.cs new file mode 100644 index 00000000..6bf8060f --- /dev/null +++ b/src/Imagekit/Models/Files/Bulk/BulkDeleteResponse.cs @@ -0,0 +1,81 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models.Files.Bulk; + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class BulkDeleteResponse : JsonModel +{ + /// + /// An array of fileIds that were successfully deleted. + /// + public IReadOnlyList? SuccessfullyDeletedFileIds + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>( + "successfullyDeletedFileIds" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "successfullyDeletedFileIds", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.SuccessfullyDeletedFileIds; + } + + public BulkDeleteResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public BulkDeleteResponse(BulkDeleteResponse bulkDeleteResponse) + : base(bulkDeleteResponse) { } +#pragma warning restore CS8618 + + public BulkDeleteResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + BulkDeleteResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static BulkDeleteResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class BulkDeleteResponseFromRaw : IFromRawJson +{ + /// + public BulkDeleteResponse FromRawUnchecked(IReadOnlyDictionary rawData) => + BulkDeleteResponse.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Files/Bulk/BulkRemoveAITagsParams.cs b/src/Imagekit/Models/Files/Bulk/BulkRemoveAITagsParams.cs new file mode 100644 index 00000000..b423e281 --- /dev/null +++ b/src/Imagekit/Models/Files/Bulk/BulkRemoveAITagsParams.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; +using Text = System.Text; + +namespace Imagekit.Models.Files.Bulk; + +/// +/// This API removes AITags from multiple files in bulk. A maximum of 50 files can +/// be specified at a time. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class BulkRemoveAITagsParams : ParamsBase +{ + readonly JsonDictionary _rawBodyData = new(); + public IReadOnlyDictionary RawBodyData + { + get { return this._rawBodyData.Freeze(); } + } + + /// + /// An array of AITags that you want to remove from the files. + /// + public required IReadOnlyList AITags + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullStruct>("AITags"); + } + init + { + this._rawBodyData.Set>( + "AITags", + ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// An array of fileIds from which you want to remove AITags. + /// + public required IReadOnlyList FileIds + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullStruct>("fileIds"); + } + init + { + this._rawBodyData.Set>( + "fileIds", + ImmutableArray.ToImmutableArray(value) + ); + } + } + + public BulkRemoveAITagsParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public BulkRemoveAITagsParams(BulkRemoveAITagsParams bulkRemoveAITagsParams) + : base(bulkRemoveAITagsParams) + { + this._rawBodyData = new(bulkRemoveAITagsParams._rawBodyData); + } +#pragma warning restore CS8618 + + public BulkRemoveAITagsParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + BulkRemoveAITagsParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + FrozenDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } +#pragma warning restore CS8618 + + /// + public static BulkRemoveAITagsParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + FrozenDictionary.ToFrozenDictionary(rawBodyData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this._rawBodyData.Freeze()), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(BulkRemoveAITagsParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this._rawBodyData.Equals(other._rawBodyData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder(options.BaseUrl.ToString().TrimEnd('/') + "/v1/files/removeAITags") + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Files/Bulk/BulkRemoveAITagsResponse.cs b/src/Imagekit/Models/Files/Bulk/BulkRemoveAITagsResponse.cs new file mode 100644 index 00000000..3f9e6ee1 --- /dev/null +++ b/src/Imagekit/Models/Files/Bulk/BulkRemoveAITagsResponse.cs @@ -0,0 +1,84 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models.Files.Bulk; + +[JsonConverter( + typeof(JsonModelConverter) +)] +public sealed record class BulkRemoveAITagsResponse : JsonModel +{ + /// + /// An array of fileIds that in which AITags were successfully removed. + /// + public IReadOnlyList? SuccessfullyUpdatedFileIds + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>( + "successfullyUpdatedFileIds" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "successfullyUpdatedFileIds", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.SuccessfullyUpdatedFileIds; + } + + public BulkRemoveAITagsResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public BulkRemoveAITagsResponse(BulkRemoveAITagsResponse bulkRemoveAITagsResponse) + : base(bulkRemoveAITagsResponse) { } +#pragma warning restore CS8618 + + public BulkRemoveAITagsResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + BulkRemoveAITagsResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static BulkRemoveAITagsResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class BulkRemoveAITagsResponseFromRaw : IFromRawJson +{ + /// + public BulkRemoveAITagsResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) => BulkRemoveAITagsResponse.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Files/Bulk/BulkRemoveTagsParams.cs b/src/Imagekit/Models/Files/Bulk/BulkRemoveTagsParams.cs new file mode 100644 index 00000000..4e814b41 --- /dev/null +++ b/src/Imagekit/Models/Files/Bulk/BulkRemoveTagsParams.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; +using Text = System.Text; + +namespace Imagekit.Models.Files.Bulk; + +/// +/// This API removes tags from multiple files in bulk. A maximum of 50 files can be +/// specified at a time. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class BulkRemoveTagsParams : ParamsBase +{ + readonly JsonDictionary _rawBodyData = new(); + public IReadOnlyDictionary RawBodyData + { + get { return this._rawBodyData.Freeze(); } + } + + /// + /// An array of fileIds from which you want to remove tags. + /// + public required IReadOnlyList FileIds + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullStruct>("fileIds"); + } + init + { + this._rawBodyData.Set>( + "fileIds", + ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// An array of tags that you want to remove from the files. + /// + public required IReadOnlyList Tags + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullStruct>("tags"); + } + init + { + this._rawBodyData.Set>( + "tags", + ImmutableArray.ToImmutableArray(value) + ); + } + } + + public BulkRemoveTagsParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public BulkRemoveTagsParams(BulkRemoveTagsParams bulkRemoveTagsParams) + : base(bulkRemoveTagsParams) + { + this._rawBodyData = new(bulkRemoveTagsParams._rawBodyData); + } +#pragma warning restore CS8618 + + public BulkRemoveTagsParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + BulkRemoveTagsParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + FrozenDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } +#pragma warning restore CS8618 + + /// + public static BulkRemoveTagsParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + FrozenDictionary.ToFrozenDictionary(rawBodyData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this._rawBodyData.Freeze()), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(BulkRemoveTagsParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this._rawBodyData.Equals(other._rawBodyData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder(options.BaseUrl.ToString().TrimEnd('/') + "/v1/files/removeTags") + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Files/Bulk/BulkRemoveTagsResponse.cs b/src/Imagekit/Models/Files/Bulk/BulkRemoveTagsResponse.cs new file mode 100644 index 00000000..a6bc15c6 --- /dev/null +++ b/src/Imagekit/Models/Files/Bulk/BulkRemoveTagsResponse.cs @@ -0,0 +1,82 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models.Files.Bulk; + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class BulkRemoveTagsResponse : JsonModel +{ + /// + /// An array of fileIds that in which tags were successfully removed. + /// + public IReadOnlyList? SuccessfullyUpdatedFileIds + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>( + "successfullyUpdatedFileIds" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "successfullyUpdatedFileIds", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.SuccessfullyUpdatedFileIds; + } + + public BulkRemoveTagsResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public BulkRemoveTagsResponse(BulkRemoveTagsResponse bulkRemoveTagsResponse) + : base(bulkRemoveTagsResponse) { } +#pragma warning restore CS8618 + + public BulkRemoveTagsResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + BulkRemoveTagsResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static BulkRemoveTagsResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class BulkRemoveTagsResponseFromRaw : IFromRawJson +{ + /// + public BulkRemoveTagsResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) => BulkRemoveTagsResponse.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Files/File.cs b/src/Imagekit/Models/Files/File.cs new file mode 100644 index 00000000..1e0b88fa --- /dev/null +++ b/src/Imagekit/Models/Files/File.cs @@ -0,0 +1,750 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models.Files; + +/// +/// Object containing details of a file or file version. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class File : JsonModel +{ + /// + /// Array of AI-generated tags associated with the image. If no AITags are set, + /// it will be null. + /// + public IReadOnlyList? AITags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("AITags"); + } + init + { + this._rawData.Set?>( + "AITags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// The audio codec used in the video (only for video/audio). + /// + public string? AudioCodec + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("audioCodec"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("audioCodec", value); + } + } + + /// + /// The bit rate of the video in kbps (only for video). + /// + public long? BitRate + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("bitRate"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("bitRate", value); + } + } + + /// + /// Date and time when the file was uploaded. The date and time is in ISO8601 + /// format. + /// + public System::DateTimeOffset? CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("createdAt"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("createdAt", value); + } + } + + /// + /// An string with custom coordinates of the file. + /// + public string? CustomCoordinates + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("customCoordinates"); + } + init { this._rawData.Set("customCoordinates", value); } + } + + /// + /// An object with custom metadata for the file. + /// + public IReadOnlyDictionary? CustomMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "customMetadata" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "customMetadata", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Optional text to describe the contents of the file. Can be set by the user + /// or the ai-auto-description extension. + /// + public string? Description + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("description"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("description", value); + } + } + + /// + /// The duration of the video in seconds (only for video). + /// + public long? Duration + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("duration"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("duration", value); + } + } + + /// + /// Consolidated embedded metadata associated with the file. It includes exif, + /// iptc, and xmp data. + /// + public IReadOnlyDictionary? EmbeddedMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "embeddedMetadata" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "embeddedMetadata", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Unique identifier of the asset. + /// + public string? FileID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("fileId"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("fileId", value); + } + } + + /// + /// Path of the file. This is the path you would use in the URL to access the + /// file. For example, if the file is at the root of the media library, the path + /// will be `/file.jpg`. If the file is inside a folder named `images`, the path + /// will be `/images/file.jpg`. + /// + public string? FilePath + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("filePath"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("filePath", value); + } + } + + /// + /// Type of the file. Possible values are `image`, `non-image`. + /// + public string? FileType + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("fileType"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("fileType", value); + } + } + + /// + /// Specifies if the image has an alpha channel. + /// + public bool? HasAlpha + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("hasAlpha"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("hasAlpha", value); + } + } + + /// + /// Height of the file. + /// + public double? Height + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("height"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("height", value); + } + } + + /// + /// Specifies if the file is private or not. + /// + public bool? IsPrivateFile + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("isPrivateFile"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("isPrivateFile", value); + } + } + + /// + /// Specifies if the file is published or not. + /// + public bool? IsPublished + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("isPublished"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("isPublished", value); + } + } + + /// + /// MIME type of the file. + /// + public string? Mime + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("mime"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("mime", value); + } + } + + /// + /// Name of the asset. + /// + public string? Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("name"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("name", value); + } + } + + /// + /// This field is included in the response only if the Path policy feature is + /// available in the plan. It contains schema definitions for the custom metadata + /// fields selected for the specified file path. Field selection can only be + /// done when the Path policy feature is enabled. + /// + /// Keys are the names of the custom metadata fields; the value object + /// has details about the custom metadata schema. + /// + public IReadOnlyDictionary? SelectedFieldsSchema + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + FrozenDictionary + >("selectedFieldsSchema"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "selectedFieldsSchema", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Size of the file in bytes. + /// + public double? Size + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("size"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("size", value); + } + } + + /// + /// An array of tags assigned to the file. Tags are used to search files in the + /// media library. + /// + public IReadOnlyList? Tags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("tags"); + } + init + { + this._rawData.Set?>( + "tags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// URL of the thumbnail image. This URL is used to access the thumbnail image + /// of the file in the media library. + /// + public string? Thumbnail + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("thumbnail"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("thumbnail", value); + } + } + + /// + /// Type of the asset. + /// + public ApiEnum? Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum + >("type"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("type", value); + } + } + + /// + /// Date and time when the file was last updated. The date and time is in ISO8601 + /// format. + /// + public System::DateTimeOffset? UpdatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("updatedAt"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("updatedAt", value); + } + } + + /// + /// URL of the file. + /// + public string? Url + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("url"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("url", value); + } + } + + /// + /// An object with details of the file version. + /// + public VersionInfo? VersionInfo + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("versionInfo"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("versionInfo", value); + } + } + + /// + /// The video codec used in the video (only for video). + /// + public string? VideoCodec + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("videoCodec"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("videoCodec", value); + } + } + + /// + /// Width of the file. + /// + public double? Width + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("width"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("width", value); + } + } + + /// + public override void Validate() + { + foreach (var item in this.AITags ?? []) + { + item.Validate(); + } + _ = this.AudioCodec; + _ = this.BitRate; + _ = this.CreatedAt; + _ = this.CustomCoordinates; + _ = this.CustomMetadata; + _ = this.Description; + _ = this.Duration; + _ = this.EmbeddedMetadata; + _ = this.FileID; + _ = this.FilePath; + _ = this.FileType; + _ = this.HasAlpha; + _ = this.Height; + _ = this.IsPrivateFile; + _ = this.IsPublished; + _ = this.Mime; + _ = this.Name; + if (this.SelectedFieldsSchema != null) + { + foreach (var item in this.SelectedFieldsSchema.Values) + { + item.Validate(); + } + } + _ = this.Size; + _ = this.Tags; + _ = this.Thumbnail; + this.Type?.Validate(); + _ = this.UpdatedAt; + _ = this.Url; + this.VersionInfo?.Validate(); + _ = this.VideoCodec; + _ = this.Width; + } + + public File() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public File(File file) + : base(file) { } +#pragma warning restore CS8618 + + public File(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + File(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static File FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FileFromRaw : IFromRawJson +{ + /// + public File FromRawUnchecked(IReadOnlyDictionary rawData) => + File.FromRawUnchecked(rawData); +} + +/// +/// Type of the asset. +/// +[JsonConverter(typeof(TypeConverter))] +public enum Type +{ + File, + FileVersion, +} + +sealed class TypeConverter : JsonConverter +{ + public override global::Imagekit.Models.Files.Type Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "file" => global::Imagekit.Models.Files.Type.File, + "file-version" => global::Imagekit.Models.Files.Type.FileVersion, + _ => (global::Imagekit.Models.Files.Type)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + global::Imagekit.Models.Files.Type value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + global::Imagekit.Models.Files.Type.File => "file", + global::Imagekit.Models.Files.Type.FileVersion => "file-version", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} diff --git a/src/Imagekit/Models/Files/FileCopyParams.cs b/src/Imagekit/Models/Files/FileCopyParams.cs new file mode 100644 index 00000000..38f45839 --- /dev/null +++ b/src/Imagekit/Models/Files/FileCopyParams.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; +using Text = System.Text; + +namespace Imagekit.Models.Files; + +/// +/// This will copy a file from one folder to another. +/// +/// Note: If any file at the destination has the same name as the source file, +/// then the source file and its versions (if `includeFileVersions` is set to true) +/// will be appended to the destination file version history. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class FileCopyParams : ParamsBase +{ + readonly JsonDictionary _rawBodyData = new(); + public IReadOnlyDictionary RawBodyData + { + get { return this._rawBodyData.Freeze(); } + } + + /// + /// Full path to the folder you want to copy the above file into. + /// + public required string DestinationPath + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("destinationPath"); + } + init { this._rawBodyData.Set("destinationPath", value); } + } + + /// + /// The full path of the file you want to copy. + /// + public required string SourceFilePath + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("sourceFilePath"); + } + init { this._rawBodyData.Set("sourceFilePath", value); } + } + + /// + /// Option to copy all versions of a file. By default, only the current version + /// of the file is copied. When set to true, all versions of the file will be + /// copied. Default value - `false`. + /// + public bool? IncludeFileVersions + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct("includeFileVersions"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("includeFileVersions", value); + } + } + + public FileCopyParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileCopyParams(FileCopyParams fileCopyParams) + : base(fileCopyParams) + { + this._rawBodyData = new(fileCopyParams._rawBodyData); + } +#pragma warning restore CS8618 + + public FileCopyParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileCopyParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + FrozenDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } +#pragma warning restore CS8618 + + /// + public static FileCopyParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + FrozenDictionary.ToFrozenDictionary(rawBodyData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this._rawBodyData.Freeze()), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(FileCopyParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this._rawBodyData.Equals(other._rawBodyData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder(options.BaseUrl.ToString().TrimEnd('/') + "/v1/files/copy") + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Files/FileCopyResponse.cs b/src/Imagekit/Models/Files/FileCopyResponse.cs new file mode 100644 index 00000000..f484290b --- /dev/null +++ b/src/Imagekit/Models/Files/FileCopyResponse.cs @@ -0,0 +1,51 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models.Files; + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class FileCopyResponse : JsonModel +{ + /// + public override void Validate() { } + + public FileCopyResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileCopyResponse(FileCopyResponse fileCopyResponse) + : base(fileCopyResponse) { } +#pragma warning restore CS8618 + + public FileCopyResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileCopyResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FileCopyResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FileCopyResponseFromRaw : IFromRawJson +{ + /// + public FileCopyResponse FromRawUnchecked(IReadOnlyDictionary rawData) => + FileCopyResponse.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Files/FileDeleteParams.cs b/src/Imagekit/Models/Files/FileDeleteParams.cs new file mode 100644 index 00000000..ec41bc4d --- /dev/null +++ b/src/Imagekit/Models/Files/FileDeleteParams.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Models.Files; + +/// +/// This API deletes the file and all its file versions permanently. +/// +/// Note: If a file or specific transformation has been requested in the past, +/// then the response is cached. Deleting a file does not purge the cache. You can +/// purge the cache using purge cache API. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class FileDeleteParams : ParamsBase +{ + public string? FileID { get; init; } + + public FileDeleteParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileDeleteParams(FileDeleteParams fileDeleteParams) + : base(fileDeleteParams) + { + this.FileID = fileDeleteParams.FileID; + } +#pragma warning restore CS8618 + + public FileDeleteParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileDeleteParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + string fileID + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this.FileID = fileID; + } +#pragma warning restore CS8618 + + /// + public static FileDeleteParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + string fileID + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + fileID + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["FileID"] = JsonSerializer.SerializeToElement(this.FileID), + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(FileDeleteParams? other) + { + if (other == null) + { + return false; + } + return (this.FileID?.Equals(other.FileID) ?? other.FileID == null) + && this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + string.Format("/v1/files/{0}", this.FileID) + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Files/FileGetParams.cs b/src/Imagekit/Models/Files/FileGetParams.cs new file mode 100644 index 00000000..e23f53c8 --- /dev/null +++ b/src/Imagekit/Models/Files/FileGetParams.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Models.Files; + +/// +/// This API returns an object with details or attributes about the current version +/// of the file. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class FileGetParams : ParamsBase +{ + public string? FileID { get; init; } + + public FileGetParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileGetParams(FileGetParams fileGetParams) + : base(fileGetParams) + { + this.FileID = fileGetParams.FileID; + } +#pragma warning restore CS8618 + + public FileGetParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileGetParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + string fileID + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this.FileID = fileID; + } +#pragma warning restore CS8618 + + /// + public static FileGetParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + string fileID + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + fileID + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["FileID"] = JsonSerializer.SerializeToElement(this.FileID), + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(FileGetParams? other) + { + if (other == null) + { + return false; + } + return (this.FileID?.Equals(other.FileID) ?? other.FileID == null) + && this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + + string.Format("/v1/files/{0}/details", this.FileID) + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Files/FileMetadata.cs b/src/Imagekit/Models/Files/FileMetadata.cs new file mode 100644 index 00000000..16367982 --- /dev/null +++ b/src/Imagekit/Models/Files/FileMetadata.cs @@ -0,0 +1,1646 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models.Files; + +/// +/// JSON object containing metadata. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class FileMetadata : JsonModel +{ + /// + /// The audio codec used in the video (only for video). + /// + public string? AudioCodec + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("audioCodec"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("audioCodec", value); + } + } + + /// + /// The bit rate of the video in kbps (only for video). + /// + public long? BitRate + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("bitRate"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("bitRate", value); + } + } + + /// + /// The density of the image in DPI. + /// + public long? Density + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("density"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("density", value); + } + } + + /// + /// The duration of the video in seconds (only for video). + /// + public long? Duration + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("duration"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("duration", value); + } + } + + public Exif? Exif + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("exif"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("exif", value); + } + } + + /// + /// The format of the file (e.g., 'jpg', 'mp4'). + /// + public string? Format + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("format"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("format", value); + } + } + + /// + /// Indicates if the image has a color profile. + /// + public bool? HasColorProfile + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("hasColorProfile"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("hasColorProfile", value); + } + } + + /// + /// Indicates if the image contains transparent areas. + /// + public bool? HasTransparency + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("hasTransparency"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("hasTransparency", value); + } + } + + /// + /// The height of the image or video in pixels. + /// + public long? Height + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("height"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("height", value); + } + } + + /// + /// Perceptual hash of the image. + /// + public string? PHash + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("pHash"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("pHash", value); + } + } + + /// + /// The quality indicator of the image. + /// + public long? Quality + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("quality"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("quality", value); + } + } + + /// + /// The file size in bytes. + /// + public long? Size + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("size"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("size", value); + } + } + + /// + /// The video codec used in the video (only for video). + /// + public string? VideoCodec + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("videoCodec"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("videoCodec", value); + } + } + + /// + /// The width of the image or video in pixels. + /// + public long? Width + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("width"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("width", value); + } + } + + /// + public override void Validate() + { + _ = this.AudioCodec; + _ = this.BitRate; + _ = this.Density; + _ = this.Duration; + this.Exif?.Validate(); + _ = this.Format; + _ = this.HasColorProfile; + _ = this.HasTransparency; + _ = this.Height; + _ = this.PHash; + _ = this.Quality; + _ = this.Size; + _ = this.VideoCodec; + _ = this.Width; + } + + public FileMetadata() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileMetadata(FileMetadata fileMetadata) + : base(fileMetadata) { } +#pragma warning restore CS8618 + + public FileMetadata(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileMetadata(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FileMetadata FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FileMetadataFromRaw : IFromRawJson +{ + /// + public FileMetadata FromRawUnchecked(IReadOnlyDictionary rawData) => + FileMetadata.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Exif : JsonModel +{ + /// + /// Object containing Exif details. + /// + public ExifExif? ExifValue + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("exif"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("exif", value); + } + } + + /// + /// Object containing GPS information. + /// + public Gps? Gps + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("gps"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("gps", value); + } + } + + /// + /// Object containing EXIF image information. + /// + public Image? Image + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("image"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("image", value); + } + } + + /// + /// JSON object. + /// + public Interoperability? Interoperability + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("interoperability"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("interoperability", value); + } + } + + public IReadOnlyDictionary? Makernote + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "makernote" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "makernote", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Object containing Thumbnail information. + /// + public ExifThumbnail? Thumbnail + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("thumbnail"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("thumbnail", value); + } + } + + /// + public override void Validate() + { + this.ExifValue?.Validate(); + this.Gps?.Validate(); + this.Image?.Validate(); + this.Interoperability?.Validate(); + _ = this.Makernote; + this.Thumbnail?.Validate(); + } + + public Exif() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Exif(Exif exif) + : base(exif) { } +#pragma warning restore CS8618 + + public Exif(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Exif(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Exif FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ExifFromRaw : IFromRawJson +{ + /// + public Exif FromRawUnchecked(IReadOnlyDictionary rawData) => + Exif.FromRawUnchecked(rawData); +} + +/// +/// Object containing Exif details. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class ExifExif : JsonModel +{ + public double? ApertureValue + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("ApertureValue"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ApertureValue", value); + } + } + + public long? ColorSpace + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("ColorSpace"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ColorSpace", value); + } + } + + public string? CreateDate + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("CreateDate"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("CreateDate", value); + } + } + + public long? CustomRendered + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("CustomRendered"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("CustomRendered", value); + } + } + + public string? DateTimeOriginal + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("DateTimeOriginal"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("DateTimeOriginal", value); + } + } + + public long? ExifImageHeight + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("ExifImageHeight"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ExifImageHeight", value); + } + } + + public long? ExifImageWidth + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("ExifImageWidth"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ExifImageWidth", value); + } + } + + public string? ExifVersion + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("ExifVersion"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ExifVersion", value); + } + } + + public double? ExposureCompensation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("ExposureCompensation"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ExposureCompensation", value); + } + } + + public long? ExposureMode + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("ExposureMode"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ExposureMode", value); + } + } + + public long? ExposureProgram + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("ExposureProgram"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ExposureProgram", value); + } + } + + public double? ExposureTime + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("ExposureTime"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ExposureTime", value); + } + } + + public long? Flash + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("Flash"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("Flash", value); + } + } + + public string? FlashpixVersion + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("FlashpixVersion"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("FlashpixVersion", value); + } + } + + public double? FNumber + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("FNumber"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("FNumber", value); + } + } + + public long? FocalLength + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("FocalLength"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("FocalLength", value); + } + } + + public long? FocalPlaneResolutionUnit + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("FocalPlaneResolutionUnit"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("FocalPlaneResolutionUnit", value); + } + } + + public double? FocalPlaneXResolution + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("FocalPlaneXResolution"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("FocalPlaneXResolution", value); + } + } + + public double? FocalPlaneYResolution + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("FocalPlaneYResolution"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("FocalPlaneYResolution", value); + } + } + + public long? InteropOffset + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("InteropOffset"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("InteropOffset", value); + } + } + + public long? Iso + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("ISO"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ISO", value); + } + } + + public long? MeteringMode + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("MeteringMode"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("MeteringMode", value); + } + } + + public long? SceneCaptureType + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("SceneCaptureType"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("SceneCaptureType", value); + } + } + + public double? ShutterSpeedValue + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("ShutterSpeedValue"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ShutterSpeedValue", value); + } + } + + public string? SubSecTime + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("SubSecTime"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("SubSecTime", value); + } + } + + public long? WhiteBalance + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("WhiteBalance"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("WhiteBalance", value); + } + } + + /// + public override void Validate() + { + _ = this.ApertureValue; + _ = this.ColorSpace; + _ = this.CreateDate; + _ = this.CustomRendered; + _ = this.DateTimeOriginal; + _ = this.ExifImageHeight; + _ = this.ExifImageWidth; + _ = this.ExifVersion; + _ = this.ExposureCompensation; + _ = this.ExposureMode; + _ = this.ExposureProgram; + _ = this.ExposureTime; + _ = this.Flash; + _ = this.FlashpixVersion; + _ = this.FNumber; + _ = this.FocalLength; + _ = this.FocalPlaneResolutionUnit; + _ = this.FocalPlaneXResolution; + _ = this.FocalPlaneYResolution; + _ = this.InteropOffset; + _ = this.Iso; + _ = this.MeteringMode; + _ = this.SceneCaptureType; + _ = this.ShutterSpeedValue; + _ = this.SubSecTime; + _ = this.WhiteBalance; + } + + public ExifExif() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExifExif(ExifExif exifExif) + : base(exifExif) { } +#pragma warning restore CS8618 + + public ExifExif(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExifExif(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExifExif FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ExifExifFromRaw : IFromRawJson +{ + /// + public ExifExif FromRawUnchecked(IReadOnlyDictionary rawData) => + ExifExif.FromRawUnchecked(rawData); +} + +/// +/// Object containing GPS information. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Gps : JsonModel +{ + public IReadOnlyList? GpsVersionID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("GPSVersionID"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "GPSVersionID", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.GpsVersionID; + } + + public Gps() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Gps(Gps gps) + : base(gps) { } +#pragma warning restore CS8618 + + public Gps(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Gps(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Gps FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class GpsFromRaw : IFromRawJson +{ + /// + public Gps FromRawUnchecked(IReadOnlyDictionary rawData) => + Gps.FromRawUnchecked(rawData); +} + +/// +/// Object containing EXIF image information. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Image : JsonModel +{ + public long? ExifOffset + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("ExifOffset"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ExifOffset", value); + } + } + + public long? GpsInfo + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("GPSInfo"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("GPSInfo", value); + } + } + + public string? Make + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("Make"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("Make", value); + } + } + + public string? Model + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("Model"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("Model", value); + } + } + + public string? ModifyDate + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("ModifyDate"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ModifyDate", value); + } + } + + public long? Orientation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("Orientation"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("Orientation", value); + } + } + + public long? ResolutionUnit + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("ResolutionUnit"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ResolutionUnit", value); + } + } + + public string? Software + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("Software"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("Software", value); + } + } + + public long? XResolution + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("XResolution"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("XResolution", value); + } + } + + public long? YCbCrPositioning + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("YCbCrPositioning"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("YCbCrPositioning", value); + } + } + + public long? YResolution + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("YResolution"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("YResolution", value); + } + } + + /// + public override void Validate() + { + _ = this.ExifOffset; + _ = this.GpsInfo; + _ = this.Make; + _ = this.Model; + _ = this.ModifyDate; + _ = this.Orientation; + _ = this.ResolutionUnit; + _ = this.Software; + _ = this.XResolution; + _ = this.YCbCrPositioning; + _ = this.YResolution; + } + + public Image() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Image(Image image) + : base(image) { } +#pragma warning restore CS8618 + + public Image(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Image(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Image FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ImageFromRaw : IFromRawJson +{ + /// + public Image FromRawUnchecked(IReadOnlyDictionary rawData) => + Image.FromRawUnchecked(rawData); +} + +/// +/// JSON object. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Interoperability : JsonModel +{ + public string? InteropIndex + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("InteropIndex"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("InteropIndex", value); + } + } + + public string? InteropVersion + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("InteropVersion"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("InteropVersion", value); + } + } + + /// + public override void Validate() + { + _ = this.InteropIndex; + _ = this.InteropVersion; + } + + public Interoperability() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Interoperability(Interoperability interoperability) + : base(interoperability) { } +#pragma warning restore CS8618 + + public Interoperability(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Interoperability(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Interoperability FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class InteroperabilityFromRaw : IFromRawJson +{ + /// + public Interoperability FromRawUnchecked(IReadOnlyDictionary rawData) => + Interoperability.FromRawUnchecked(rawData); +} + +/// +/// Object containing Thumbnail information. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class ExifThumbnail : JsonModel +{ + public long? Compression + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("Compression"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("Compression", value); + } + } + + public long? ResolutionUnit + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("ResolutionUnit"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ResolutionUnit", value); + } + } + + public long? ThumbnailLength + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("ThumbnailLength"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ThumbnailLength", value); + } + } + + public long? ThumbnailOffset + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("ThumbnailOffset"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ThumbnailOffset", value); + } + } + + public long? XResolution + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("XResolution"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("XResolution", value); + } + } + + public long? YResolution + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("YResolution"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("YResolution", value); + } + } + + /// + public override void Validate() + { + _ = this.Compression; + _ = this.ResolutionUnit; + _ = this.ThumbnailLength; + _ = this.ThumbnailOffset; + _ = this.XResolution; + _ = this.YResolution; + } + + public ExifThumbnail() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExifThumbnail(ExifThumbnail exifThumbnail) + : base(exifThumbnail) { } +#pragma warning restore CS8618 + + public ExifThumbnail(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExifThumbnail(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExifThumbnail FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ExifThumbnailFromRaw : IFromRawJson +{ + /// + public ExifThumbnail FromRawUnchecked(IReadOnlyDictionary rawData) => + ExifThumbnail.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Files/FileMoveParams.cs b/src/Imagekit/Models/Files/FileMoveParams.cs new file mode 100644 index 00000000..ccd5c555 --- /dev/null +++ b/src/Imagekit/Models/Files/FileMoveParams.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; +using Text = System.Text; + +namespace Imagekit.Models.Files; + +/// +/// This will move a file and all its versions from one folder to another. +/// +/// Note: If any file at the destination has the same name as the source file, +/// then the source file and its versions will be appended to the destination file. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class FileMoveParams : ParamsBase +{ + readonly JsonDictionary _rawBodyData = new(); + public IReadOnlyDictionary RawBodyData + { + get { return this._rawBodyData.Freeze(); } + } + + /// + /// Full path to the folder you want to move the above file into. + /// + public required string DestinationPath + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("destinationPath"); + } + init { this._rawBodyData.Set("destinationPath", value); } + } + + /// + /// The full path of the file you want to move. + /// + public required string SourceFilePath + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("sourceFilePath"); + } + init { this._rawBodyData.Set("sourceFilePath", value); } + } + + public FileMoveParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileMoveParams(FileMoveParams fileMoveParams) + : base(fileMoveParams) + { + this._rawBodyData = new(fileMoveParams._rawBodyData); + } +#pragma warning restore CS8618 + + public FileMoveParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileMoveParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + FrozenDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } +#pragma warning restore CS8618 + + /// + public static FileMoveParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + FrozenDictionary.ToFrozenDictionary(rawBodyData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this._rawBodyData.Freeze()), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(FileMoveParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this._rawBodyData.Equals(other._rawBodyData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder(options.BaseUrl.ToString().TrimEnd('/') + "/v1/files/move") + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Files/FileMoveResponse.cs b/src/Imagekit/Models/Files/FileMoveResponse.cs new file mode 100644 index 00000000..6e6b75e7 --- /dev/null +++ b/src/Imagekit/Models/Files/FileMoveResponse.cs @@ -0,0 +1,51 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models.Files; + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class FileMoveResponse : JsonModel +{ + /// + public override void Validate() { } + + public FileMoveResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileMoveResponse(FileMoveResponse fileMoveResponse) + : base(fileMoveResponse) { } +#pragma warning restore CS8618 + + public FileMoveResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileMoveResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FileMoveResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FileMoveResponseFromRaw : IFromRawJson +{ + /// + public FileMoveResponse FromRawUnchecked(IReadOnlyDictionary rawData) => + FileMoveResponse.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Files/FileRenameParams.cs b/src/Imagekit/Models/Files/FileRenameParams.cs new file mode 100644 index 00000000..24ab5f8e --- /dev/null +++ b/src/Imagekit/Models/Files/FileRenameParams.cs @@ -0,0 +1,205 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; +using Text = System.Text; + +namespace Imagekit.Models.Files; + +/// +/// You can rename an already existing file in the media library using rename file +/// API. This operation would rename all file versions of the file. +/// +/// Note: The old URLs will stop working. The file/file version URLs cached +/// on CDN will continue to work unless a purge is requested. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class FileRenameParams : ParamsBase +{ + readonly JsonDictionary _rawBodyData = new(); + public IReadOnlyDictionary RawBodyData + { + get { return this._rawBodyData.Freeze(); } + } + + /// + /// The full path of the file you want to rename. + /// + public required string FilePath + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("filePath"); + } + init { this._rawBodyData.Set("filePath", value); } + } + + /// + /// The new name of the file. A filename can contain: + /// + /// Alphanumeric Characters: `a-z`, `A-Z`, `0-9` (including Unicode letters, + /// marks, and numerals in other languages). Special Characters: `.`, `_`, and `-`. + /// + /// Any other character, including space, will be replaced by `_`. + /// + public required string NewFileName + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("newFileName"); + } + init { this._rawBodyData.Set("newFileName", value); } + } + + /// + /// Option to purge cache for the old file and its versions' URLs. + /// + /// When set to true, it will internally issue a purge cache request on + /// CDN to remove cached content of old file and its versions. This purge request + /// is counted against your monthly purge quota. + /// + /// Note: If the old file were accessible at `https://ik.imagekit.io/demo/old-filename.jpg`, + /// a purge cache request would be issued against `https://ik.imagekit.io/demo/old-filename.jpg*` + /// (with a wildcard at the end). It will remove the file and its versions' URLs + /// and any transformations made using query parameters on this file or its versions. + /// However, the cache for file transformations made using path parameters will + /// persist. You can purge them using the purge API. For more details, refer + /// to the purge API documentation. + /// + /// Default value - `false` + /// + public bool? PurgeCache + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct("purgeCache"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("purgeCache", value); + } + } + + public FileRenameParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileRenameParams(FileRenameParams fileRenameParams) + : base(fileRenameParams) + { + this._rawBodyData = new(fileRenameParams._rawBodyData); + } +#pragma warning restore CS8618 + + public FileRenameParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileRenameParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + FrozenDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } +#pragma warning restore CS8618 + + /// + public static FileRenameParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + FrozenDictionary.ToFrozenDictionary(rawBodyData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this._rawBodyData.Freeze()), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(FileRenameParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this._rawBodyData.Equals(other._rawBodyData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder(options.BaseUrl.ToString().TrimEnd('/') + "/v1/files/rename") + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Files/FileRenameResponse.cs b/src/Imagekit/Models/Files/FileRenameResponse.cs new file mode 100644 index 00000000..a0d92c8f --- /dev/null +++ b/src/Imagekit/Models/Files/FileRenameResponse.cs @@ -0,0 +1,76 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models.Files; + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class FileRenameResponse : JsonModel +{ + /// + /// Unique identifier of the purge request. This can be used to check the status + /// of the purge request. + /// + public string? PurgeRequestID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("purgeRequestId"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("purgeRequestId", value); + } + } + + /// + public override void Validate() + { + _ = this.PurgeRequestID; + } + + public FileRenameResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileRenameResponse(FileRenameResponse fileRenameResponse) + : base(fileRenameResponse) { } +#pragma warning restore CS8618 + + public FileRenameResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileRenameResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FileRenameResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FileRenameResponseFromRaw : IFromRawJson +{ + /// + public FileRenameResponse FromRawUnchecked(IReadOnlyDictionary rawData) => + FileRenameResponse.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Files/FileUpdateParams.cs b/src/Imagekit/Models/Files/FileUpdateParams.cs new file mode 100644 index 00000000..d6de8c0d --- /dev/null +++ b/src/Imagekit/Models/Files/FileUpdateParams.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; +using Text = System.Text; + +namespace Imagekit.Models.Files; + +/// +/// This API updates the details or attributes of the current version of the file. +/// You can update `tags`, `customCoordinates`, `customMetadata`, publication status, +/// remove existing `AITags` and apply extensions using this API. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class FileUpdateParams : ParamsBase +{ + public JsonElement RawBodyData { get; private init; } + + public string? FileID { get; init; } + + /// + /// Schema for update file update request. + /// + public required UpdateFileRequest UpdateFileRequest + { + get + { + return WrappedJsonSerializer.GetNotNullClass( + this.RawBodyData, + "RawBodyData" + ); + } + init { this.RawBodyData = JsonSerializer.SerializeToElement(value); } + } + + public FileUpdateParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileUpdateParams(FileUpdateParams fileUpdateParams) + : base(fileUpdateParams) + { + this.FileID = fileUpdateParams.FileID; + + this.RawBodyData = fileUpdateParams.RawBodyData; + } +#pragma warning restore CS8618 + + public FileUpdateParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + JsonElement rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this.RawBodyData = rawBodyData; + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileUpdateParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + JsonElement rawBodyData, + string fileID + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this.RawBodyData = rawBodyData; + this.FileID = fileID; + } +#pragma warning restore CS8618 + + /// + public static FileUpdateParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + JsonElement rawBodyData, + string fileID + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + rawBodyData, + fileID + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["FileID"] = JsonSerializer.SerializeToElement(this.FileID), + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this.RawBodyData), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(FileUpdateParams? other) + { + if (other == null) + { + return false; + } + return (this.FileID?.Equals(other.FileID) ?? other.FileID == null) + && this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this.RawBodyData.Equals(other.RawBodyData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + + string.Format("/v1/files/{0}/details", this.FileID) + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Files/FileUpdateResponse.cs b/src/Imagekit/Models/Files/FileUpdateResponse.cs new file mode 100644 index 00000000..2d9a646c --- /dev/null +++ b/src/Imagekit/Models/Files/FileUpdateResponse.cs @@ -0,0 +1,1199 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models.Files; + +/// +/// Object containing details of a file or file version. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class FileUpdateResponse : JsonModel +{ + /// + /// Array of AI-generated tags associated with the image. If no AITags are set, + /// it will be null. + /// + public IReadOnlyList? AITags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("AITags"); + } + init + { + this._rawData.Set?>( + "AITags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// The audio codec used in the video (only for video/audio). + /// + public string? AudioCodec + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("audioCodec"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("audioCodec", value); + } + } + + /// + /// The bit rate of the video in kbps (only for video). + /// + public long? BitRate + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("bitRate"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("bitRate", value); + } + } + + /// + /// Date and time when the file was uploaded. The date and time is in ISO8601 + /// format. + /// + public System::DateTimeOffset? CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("createdAt"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("createdAt", value); + } + } + + /// + /// An string with custom coordinates of the file. + /// + public string? CustomCoordinates + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("customCoordinates"); + } + init { this._rawData.Set("customCoordinates", value); } + } + + /// + /// A key-value data associated with the asset. + /// + public IReadOnlyDictionary? CustomMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "customMetadata" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "customMetadata", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Optional text to describe the contents of the file. Can be set by the user + /// or the ai-auto-description extension. + /// + public string? Description + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("description"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("description", value); + } + } + + /// + /// The duration of the video in seconds (only for video). + /// + public long? Duration + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("duration"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("duration", value); + } + } + + /// + /// Consolidated embedded metadata associated with the file. It includes exif, + /// iptc, and xmp data. + /// + public IReadOnlyDictionary? EmbeddedMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "embeddedMetadata" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "embeddedMetadata", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Unique identifier of the asset. + /// + public string? FileID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("fileId"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("fileId", value); + } + } + + /// + /// Path of the file. This is the path you would use in the URL to access the + /// file. For example, if the file is at the root of the media library, the path + /// will be `/file.jpg`. If the file is inside a folder named `images`, the path + /// will be `/images/file.jpg`. + /// + public string? FilePath + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("filePath"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("filePath", value); + } + } + + /// + /// Type of the file. Possible values are `image`, `non-image`. + /// + public string? FileType + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("fileType"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("fileType", value); + } + } + + /// + /// Specifies if the image has an alpha channel. + /// + public bool? HasAlpha + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("hasAlpha"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("hasAlpha", value); + } + } + + /// + /// Height of the file. + /// + public double? Height + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("height"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("height", value); + } + } + + /// + /// Specifies if the file is private or not. + /// + public bool? IsPrivateFile + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("isPrivateFile"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("isPrivateFile", value); + } + } + + /// + /// Specifies if the file is published or not. + /// + public bool? IsPublished + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("isPublished"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("isPublished", value); + } + } + + /// + /// MIME type of the file. + /// + public string? Mime + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("mime"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("mime", value); + } + } + + /// + /// Name of the asset. + /// + public string? Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("name"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("name", value); + } + } + + /// + /// This field is included in the response only if the Path policy feature is + /// available in the plan. It contains schema definitions for the custom metadata + /// fields selected for the specified file path. Field selection can only be + /// done when the Path policy feature is enabled. + /// + /// Keys are the names of the custom metadata fields; the value object + /// has details about the custom metadata schema. + /// + public IReadOnlyDictionary? SelectedFieldsSchema + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + FrozenDictionary + >("selectedFieldsSchema"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "selectedFieldsSchema", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Size of the file in bytes. + /// + public double? Size + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("size"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("size", value); + } + } + + /// + /// An array of tags assigned to the file. Tags are used to search files in the + /// media library. + /// + public IReadOnlyList? Tags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("tags"); + } + init + { + this._rawData.Set?>( + "tags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// URL of the thumbnail image. This URL is used to access the thumbnail image + /// of the file in the media library. + /// + public string? Thumbnail + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("thumbnail"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("thumbnail", value); + } + } + + /// + /// Type of the asset. + /// + public ApiEnum? Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("type"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("type", value); + } + } + + /// + /// Date and time when the file was last updated. The date and time is in ISO8601 + /// format. + /// + public System::DateTimeOffset? UpdatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("updatedAt"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("updatedAt", value); + } + } + + /// + /// URL of the file. + /// + public string? Url + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("url"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("url", value); + } + } + + /// + /// An object containing the file or file version's `id` (versionId) and `name`. + /// + public VersionInfo? VersionInfo + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("versionInfo"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("versionInfo", value); + } + } + + /// + /// The video codec used in the video (only for video). + /// + public string? VideoCodec + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("videoCodec"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("videoCodec", value); + } + } + + /// + /// Width of the file. + /// + public double? Width + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("width"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("width", value); + } + } + + public ExtensionStatus? ExtensionStatus + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("extensionStatus"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("extensionStatus", value); + } + } + + public static implicit operator File(FileUpdateResponse fileUpdateResponse) => + new() + { + AITags = fileUpdateResponse.AITags, + AudioCodec = fileUpdateResponse.AudioCodec, + BitRate = fileUpdateResponse.BitRate, + CreatedAt = fileUpdateResponse.CreatedAt, + CustomCoordinates = fileUpdateResponse.CustomCoordinates, + CustomMetadata = fileUpdateResponse.CustomMetadata, + Description = fileUpdateResponse.Description, + Duration = fileUpdateResponse.Duration, + EmbeddedMetadata = fileUpdateResponse.EmbeddedMetadata, + FileID = fileUpdateResponse.FileID, + FilePath = fileUpdateResponse.FilePath, + FileType = fileUpdateResponse.FileType, + HasAlpha = fileUpdateResponse.HasAlpha, + Height = fileUpdateResponse.Height, + IsPrivateFile = fileUpdateResponse.IsPrivateFile, + IsPublished = fileUpdateResponse.IsPublished, + Mime = fileUpdateResponse.Mime, + Name = fileUpdateResponse.Name, + SelectedFieldsSchema = fileUpdateResponse.SelectedFieldsSchema, + Size = fileUpdateResponse.Size, + Tags = fileUpdateResponse.Tags, + Thumbnail = fileUpdateResponse.Thumbnail, + Type = fileUpdateResponse.Type, + UpdatedAt = fileUpdateResponse.UpdatedAt, + Url = fileUpdateResponse.Url, + VersionInfo = fileUpdateResponse.VersionInfo, + VideoCodec = fileUpdateResponse.VideoCodec, + Width = fileUpdateResponse.Width, + }; + + /// + public override void Validate() + { + foreach (var item in this.AITags ?? []) + { + item.Validate(); + } + _ = this.AudioCodec; + _ = this.BitRate; + _ = this.CreatedAt; + _ = this.CustomCoordinates; + _ = this.CustomMetadata; + _ = this.Description; + _ = this.Duration; + _ = this.EmbeddedMetadata; + _ = this.FileID; + _ = this.FilePath; + _ = this.FileType; + _ = this.HasAlpha; + _ = this.Height; + _ = this.IsPrivateFile; + _ = this.IsPublished; + _ = this.Mime; + _ = this.Name; + if (this.SelectedFieldsSchema != null) + { + foreach (var item in this.SelectedFieldsSchema.Values) + { + item.Validate(); + } + } + _ = this.Size; + _ = this.Tags; + _ = this.Thumbnail; + this.Type?.Validate(); + _ = this.UpdatedAt; + _ = this.Url; + this.VersionInfo?.Validate(); + _ = this.VideoCodec; + _ = this.Width; + this.ExtensionStatus?.Validate(); + } + + public FileUpdateResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileUpdateResponse(FileUpdateResponse fileUpdateResponse) + : base(fileUpdateResponse) { } +#pragma warning restore CS8618 + + public FileUpdateResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileUpdateResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FileUpdateResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FileUpdateResponseFromRaw : IFromRawJson +{ + /// + public FileUpdateResponse FromRawUnchecked(IReadOnlyDictionary rawData) => + FileUpdateResponse.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter< + FileUpdateResponseFileUpdateResponse, + FileUpdateResponseFileUpdateResponseFromRaw + >) +)] +public sealed record class FileUpdateResponseFileUpdateResponse : JsonModel +{ + public ExtensionStatus? ExtensionStatus + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("extensionStatus"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("extensionStatus", value); + } + } + + /// + public override void Validate() + { + this.ExtensionStatus?.Validate(); + } + + public FileUpdateResponseFileUpdateResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileUpdateResponseFileUpdateResponse( + FileUpdateResponseFileUpdateResponse fileUpdateResponseFileUpdateResponse + ) + : base(fileUpdateResponseFileUpdateResponse) { } +#pragma warning restore CS8618 + + public FileUpdateResponseFileUpdateResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileUpdateResponseFileUpdateResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FileUpdateResponseFileUpdateResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FileUpdateResponseFileUpdateResponseFromRaw + : IFromRawJson +{ + /// + public FileUpdateResponseFileUpdateResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) => FileUpdateResponseFileUpdateResponse.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class ExtensionStatus : JsonModel +{ + public ApiEnum? AIAutoDescription + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "ai-auto-description" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ai-auto-description", value); + } + } + + public ApiEnum? AITasks + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("ai-tasks"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ai-tasks", value); + } + } + + public ApiEnum? AwsAutoTagging + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "aws-auto-tagging" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("aws-auto-tagging", value); + } + } + + public ApiEnum? GoogleAutoTagging + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "google-auto-tagging" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("google-auto-tagging", value); + } + } + + public ApiEnum? RemoveBg + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("remove-bg"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("remove-bg", value); + } + } + + /// + public override void Validate() + { + this.AIAutoDescription?.Validate(); + this.AITasks?.Validate(); + this.AwsAutoTagging?.Validate(); + this.GoogleAutoTagging?.Validate(); + this.RemoveBg?.Validate(); + } + + public ExtensionStatus() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExtensionStatus(ExtensionStatus extensionStatus) + : base(extensionStatus) { } +#pragma warning restore CS8618 + + public ExtensionStatus(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExtensionStatus(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExtensionStatus FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ExtensionStatusFromRaw : IFromRawJson +{ + /// + public ExtensionStatus FromRawUnchecked(IReadOnlyDictionary rawData) => + ExtensionStatus.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(AIAutoDescriptionConverter))] +public enum AIAutoDescription +{ + Success, + Pending, + Failed, +} + +sealed class AIAutoDescriptionConverter : JsonConverter +{ + public override AIAutoDescription Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "success" => AIAutoDescription.Success, + "pending" => AIAutoDescription.Pending, + "failed" => AIAutoDescription.Failed, + _ => (AIAutoDescription)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + AIAutoDescription value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + AIAutoDescription.Success => "success", + AIAutoDescription.Pending => "pending", + AIAutoDescription.Failed => "failed", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +[JsonConverter(typeof(AITasksConverter))] +public enum AITasks +{ + Success, + Pending, + Failed, +} + +sealed class AITasksConverter : JsonConverter +{ + public override AITasks Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "success" => AITasks.Success, + "pending" => AITasks.Pending, + "failed" => AITasks.Failed, + _ => (AITasks)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, AITasks value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + AITasks.Success => "success", + AITasks.Pending => "pending", + AITasks.Failed => "failed", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +[JsonConverter(typeof(AwsAutoTaggingConverter))] +public enum AwsAutoTagging +{ + Success, + Pending, + Failed, +} + +sealed class AwsAutoTaggingConverter : JsonConverter +{ + public override AwsAutoTagging Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "success" => AwsAutoTagging.Success, + "pending" => AwsAutoTagging.Pending, + "failed" => AwsAutoTagging.Failed, + _ => (AwsAutoTagging)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + AwsAutoTagging value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + AwsAutoTagging.Success => "success", + AwsAutoTagging.Pending => "pending", + AwsAutoTagging.Failed => "failed", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +[JsonConverter(typeof(GoogleAutoTaggingConverter))] +public enum GoogleAutoTagging +{ + Success, + Pending, + Failed, +} + +sealed class GoogleAutoTaggingConverter : JsonConverter +{ + public override GoogleAutoTagging Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "success" => GoogleAutoTagging.Success, + "pending" => GoogleAutoTagging.Pending, + "failed" => GoogleAutoTagging.Failed, + _ => (GoogleAutoTagging)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + GoogleAutoTagging value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + GoogleAutoTagging.Success => "success", + GoogleAutoTagging.Pending => "pending", + GoogleAutoTagging.Failed => "failed", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +[JsonConverter(typeof(RemoveBgConverter))] +public enum RemoveBg +{ + Success, + Pending, + Failed, +} + +sealed class RemoveBgConverter : JsonConverter +{ + public override RemoveBg Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "success" => RemoveBg.Success, + "pending" => RemoveBg.Pending, + "failed" => RemoveBg.Failed, + _ => (RemoveBg)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, RemoveBg value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + RemoveBg.Success => "success", + RemoveBg.Pending => "pending", + RemoveBg.Failed => "failed", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} diff --git a/src/Imagekit/Models/Files/FileUploadParams.cs b/src/Imagekit/Models/Files/FileUploadParams.cs new file mode 100644 index 00000000..c70c83d0 --- /dev/null +++ b/src/Imagekit/Models/Files/FileUploadParams.cs @@ -0,0 +1,1700 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Helper; +using System = System; + +namespace Imagekit.Models.Files; + +/// +/// ImageKit.io allows you to upload files directly from both the server and client +/// sides. For server-side uploads, private API key authentication is used. For client-side +/// uploads, generate a one-time `token`, `signature`, and `expire` from your secure +/// backend using private API. [Learn more](/docs/api-reference/upload-file/upload-file#how-to-implement-client-side-file-upload) +/// about how to implement client-side file upload. +/// +/// The [V2 API](/docs/api-reference/upload-file/upload-file-v2) enhances security +/// by verifying the entire payload using JWT. +/// +/// **File size limit** \ On the free plan, the maximum upload file sizes are +/// 25MB for images, audio, and raw files and 100MB for videos. On the Lite paid plan, +/// these limits increase to 40MB for images, audio, and raw files and 300MB for +/// videos, whereas on the Pro paid plan, these limits increase to 50MB for images, +/// audio, and raw files and 2GB for videos. These limits can be further increased +/// with enterprise plans. +/// +/// **Version limit** \ A file can have a maximum of 100 versions. +/// +/// **Demo applications** +/// +/// - A full-fledged [upload widget using Uppy](https://github.com/imagekit-samples/uppy-uploader), +/// supporting file selections from local storage, URL, Dropbox, Google Drive, Instagram, +/// and more. - [Quick start guides](/docs/quick-start-guides) for various frameworks +/// and technologies. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class FileUploadParams : ParamsBase +{ + readonly MultipartJsonDictionary _rawBodyData = new(); + public IReadOnlyDictionary RawBodyData + { + get { return this._rawBodyData.Freeze(); } + } + + /// + /// The API accepts any of the following: + /// + /// - **Binary data** – send the raw bytes as `multipart/form-data`. - **HTTP + /// / HTTPS URL** – a publicly reachable URL that ImageKit’s servers can fetch. + /// - **Base64 string** – the file encoded as a Base64 data URI or plain Base64. + /// + /// When supplying a URL, the server must receive the response headers within + /// 8 seconds; otherwise the request fails with 400 Bad Request. + /// + public required BinaryContent File + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("file"); + } + init { this._rawBodyData.Set("file", value); } + } + + /// + /// The name with which the file has to be uploaded. The file name can contain: + /// + /// - Alphanumeric Characters: `a-z`, `A-Z`, `0-9`. - Special Characters: + /// `.`, `-` + /// + /// Any other character including space will be replaced by `_` + /// + public required string FileName + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("fileName"); + } + init { this._rawBodyData.Set("fileName", value); } + } + + /// + /// A unique value that the ImageKit.io server will use to recognize and prevent + /// subsequent retries for the same request. We suggest using V4 UUIDs, or another + /// random string with enough entropy to avoid collisions. This field is only + /// required for authentication when uploading a file from the client side. + /// + /// **Note**: Sending a value that has been used in the past will result + /// in a validation error. Even if your previous request resulted in an error, + /// you should always send a new value for this field. + /// + public string? Token + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("token"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("token", value); + } + } + + /// + /// Server-side checks to run on the asset. Read more about [Upload API checks](/docs/api-reference/upload-file/upload-file#upload-api-checks). + /// + public string? Checks + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("checks"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("checks", value); + } + } + + /// + /// Define an important area in the image. This is only relevant for image type files. + /// + /// - To be passed as a string with the x and y coordinates of the top-left + /// corner, and width and height of the area of interest in the format `x,y,width,height`. + /// For example - `10,10,100,100` - Can be used with fo-customtransformation. + /// - If this field is not specified and the file is overwritten, then customCoordinates + /// will be removed. + /// + public string? CustomCoordinates + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("customCoordinates"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("customCoordinates", value); + } + } + + /// + /// JSON key-value pairs to associate with the asset. Create the custom metadata + /// fields before setting these values. + /// + public IReadOnlyDictionary? CustomMetadata + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass>( + "customMetadata" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set?>( + "customMetadata", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Optional text to describe the contents of the file. + /// + public string? Description + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("description"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("description", value); + } + } + + /// + /// The time until your signature is valid. It must be a [Unix time](https://en.wikipedia.org/wiki/Unix_time) + /// in less than 1 hour into the future. It should be in seconds. This field + /// is only required for authentication when uploading a file from the client + /// side. + /// + public long? Expire + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct("expire"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("expire", value); + } + } + + /// + /// Array of extensions to be applied to the asset. Each extension can be configured + /// with specific parameters based on the extension type. + /// + public IReadOnlyList? Extensions + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct>("extensions"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set?>( + "extensions", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// The folder path in which the image has to be uploaded. If the folder(s) didn't + /// exist before, a new folder(s) is created. + /// + /// The folder name can contain: + /// + /// - Alphanumeric Characters: `a-z` , `A-Z` , `0-9` - Special Characters: + /// `/` , `_` , `-` + /// + /// Using multiple `/` creates a nested folder. + /// + public string? Folder + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("folder"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("folder", value); + } + } + + /// + /// Whether to mark the file as private or not. + /// + /// If `true`, the file is marked as private and is accessible only using + /// named transformation or signed URL. + /// + public bool? IsPrivateFile + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct("isPrivateFile"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("isPrivateFile", value); + } + } + + /// + /// Whether to upload file as published or not. + /// + /// If `false`, the file is marked as unpublished, which restricts access + /// to the file only via the media library. Files in draft or unpublished state + /// can only be publicly accessed after being published. + /// + /// The option to upload in draft state is only available in custom enterprise + /// pricing plans. + /// + public bool? IsPublished + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct("isPublished"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("isPublished", value); + } + } + + /// + /// If set to `true` and a file already exists at the exact location, its AITags + /// will be removed. Set `overwriteAITags` to `false` to preserve AITags. + /// + public bool? OverwriteAITags + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct("overwriteAITags"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("overwriteAITags", value); + } + } + + /// + /// If the request does not have `customMetadata`, and a file already exists + /// at the exact location, existing customMetadata will be removed. + /// + public bool? OverwriteCustomMetadata + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct("overwriteCustomMetadata"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("overwriteCustomMetadata", value); + } + } + + /// + /// If `false` and `useUniqueFileName` is also `false`, and a file already exists + /// at the exact location, upload API will return an error immediately. + /// + public bool? OverwriteFile + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct("overwriteFile"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("overwriteFile", value); + } + } + + /// + /// If the request does not have `tags`, and a file already exists at the exact + /// location, existing tags will be removed. + /// + public bool? OverwriteTags + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct("overwriteTags"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("overwriteTags", value); + } + } + + /// + /// Your ImageKit.io public key. This field is only required for authentication + /// when uploading a file from the client side. + /// + public string? PublicKey + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("publicKey"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("publicKey", value); + } + } + + /// + /// Array of response field keys to include in the API response body. + /// + public IReadOnlyList>? ResponseFields + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct< + ImmutableArray> + >("responseFields"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set>?>( + "responseFields", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// HMAC-SHA1 digest of the token+expire using your ImageKit.io private API key + /// as a key. Learn how to create a signature on the page below. This should + /// be in lowercase. + /// + /// Signature must be calculated on the server-side. This field is only + /// required for authentication when uploading a file from the client side. + /// + public string? Signature + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("signature"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("signature", value); + } + } + + /// + /// Set the tags while uploading the file. Provide an array of tag strings (e.g. + /// `["tag1", "tag2", "tag3"]`). The combined length of all tag characters must + /// not exceed 500, and the `%` character is not allowed. If this field is not + /// specified and the file is overwritten, the existing tags will be removed. + /// + public IReadOnlyList? Tags + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct>("tags"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set?>( + "tags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Configure pre-processing (`pre`) and post-processing (`post`) transformations. + /// + /// - `pre` — applied before the file is uploaded to the Media Library. + /// Useful for reducing file size or applying basic optimizations upfront + /// (e.g., resize, compress). + /// + /// - `post` — applied immediately after upload. Ideal for generating + /// transformed versions (like video encodes or thumbnails) in advance, so they're + /// ready for delivery without delay. + /// + /// You can mix and match any combination of post-processing types. + /// + public Transformation? Transformation + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("transformation"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("transformation", value); + } + } + + /// + /// Whether to use a unique filename for this file or not. + /// + /// If `true`, ImageKit.io will add a unique suffix to the filename parameter + /// to get a unique filename. + /// + /// If `false`, then the image is uploaded with the provided filename parameter, + /// and any existing file with the same name is replaced. + /// + public bool? UseUniqueFileName + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct("useUniqueFileName"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("useUniqueFileName", value); + } + } + + /// + /// The final status of extensions after they have completed execution will be + /// delivered to this endpoint as a POST request. [Learn more](/docs/api-reference/digital-asset-management-dam/managing-assets/update-file-details#webhook-payload-structure) + /// about the webhook payload structure. + /// + public string? WebhookUrl + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("webhookUrl"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("webhookUrl", value); + } + } + + public FileUploadParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileUploadParams(FileUploadParams fileUploadParams) + : base(fileUploadParams) + { + this._rawBodyData = new(fileUploadParams._rawBodyData); + } +#pragma warning restore CS8618 + + public FileUploadParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileUploadParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + FrozenDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } +#pragma warning restore CS8618 + + /// + public static FileUploadParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + FrozenDictionary.ToFrozenDictionary(rawBodyData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this._rawBodyData.Freeze()), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(FileUploadParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this._rawBodyData.Equals(other._rawBodyData); + } + + public override System::Uri Url(ClientOptions options) + { + return new System::UriBuilder( + UploadHelpers.GetUploadBaseUrl(options.BaseUrl).TrimEnd('/') + "/api/v1/files/upload" + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() => UploadHelpers.SerializeUploadBody(RawBodyData); + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} + +[JsonConverter(typeof(ResponseFieldConverter))] +public enum ResponseField +{ + Tags, + CustomCoordinates, + IsPrivateFile, + EmbeddedMetadata, + IsPublished, + CustomMetadata, + Metadata, + SelectedFieldsSchema, +} + +sealed class ResponseFieldConverter : JsonConverter +{ + public override ResponseField Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "tags" => ResponseField.Tags, + "customCoordinates" => ResponseField.CustomCoordinates, + "isPrivateFile" => ResponseField.IsPrivateFile, + "embeddedMetadata" => ResponseField.EmbeddedMetadata, + "isPublished" => ResponseField.IsPublished, + "customMetadata" => ResponseField.CustomMetadata, + "metadata" => ResponseField.Metadata, + "selectedFieldsSchema" => ResponseField.SelectedFieldsSchema, + _ => (ResponseField)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + ResponseField value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + ResponseField.Tags => "tags", + ResponseField.CustomCoordinates => "customCoordinates", + ResponseField.IsPrivateFile => "isPrivateFile", + ResponseField.EmbeddedMetadata => "embeddedMetadata", + ResponseField.IsPublished => "isPublished", + ResponseField.CustomMetadata => "customMetadata", + ResponseField.Metadata => "metadata", + ResponseField.SelectedFieldsSchema => "selectedFieldsSchema", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Configure pre-processing (`pre`) and post-processing (`post`) transformations. +/// +/// - `pre` — applied before the file is uploaded to the Media Library. +/// Useful for reducing file size or applying basic optimizations upfront (e.g., +/// resize, compress). +/// +/// - `post` — applied immediately after upload. Ideal for generating transformed +/// versions (like video encodes or thumbnails) in advance, so they're ready for delivery +/// without delay. +/// +/// You can mix and match any combination of post-processing types. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Transformation : JsonModel +{ + /// + /// List of transformations to apply *after* the file is uploaded. Each item + /// must match one of the following types: `transformation`, `gif-to-video`, `thumbnail`, + /// `abs`. + /// + public IReadOnlyList? Post + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("post"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "post", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Transformation string to apply before uploading the file to the Media Library. + /// Useful for optimizing files at ingestion. + /// + public string? Pre + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("pre"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("pre", value); + } + } + + /// + public override void Validate() + { + foreach (var item in this.Post ?? []) + { + item.Validate(); + } + _ = this.Pre; + } + + public Transformation() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Transformation(Transformation transformation) + : base(transformation) { } +#pragma warning restore CS8618 + + public Transformation(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Transformation(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Transformation FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class TransformationFromRaw : IFromRawJson +{ + /// + public Transformation FromRawUnchecked(IReadOnlyDictionary rawData) => + Transformation.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(PostConverter))] +public record class Post : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public JsonElement Type + { + get + { + return Match( + transformation: (x) => x.Type, + gifToVideo: (x) => x.Type, + thumbnail: (x) => x.Type, + abs: (x) => x.Type + ); + } + } + + public string? ValueValue + { + get + { + return Match( + transformation: (x) => x.ValueValue, + gifToVideo: (x) => x.Value, + thumbnail: (x) => x.Value, + abs: (x) => x.Value + ); + } + } + + public Post(PostTransformation value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Post(GifToVideo value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Post(Thumbnail value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Post(Abs value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Post(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickTransformation(out var value)) { + /// // `value` is of type `PostTransformation` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickTransformation([NotNullWhen(true)] out PostTransformation? value) + { + value = this.Value as PostTransformation; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickGifToVideo(out var value)) { + /// // `value` is of type `GifToVideo` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickGifToVideo([NotNullWhen(true)] out GifToVideo? value) + { + value = this.Value as GifToVideo; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickThumbnail(out var value)) { + /// // `value` is of type `Thumbnail` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickThumbnail([NotNullWhen(true)] out Thumbnail? value) + { + value = this.Value as Thumbnail; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickAbs(out var value)) { + /// // `value` is of type `Abs` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickAbs([NotNullWhen(true)] out Abs? value) + { + value = this.Value as Abs; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (PostTransformation value) => {...}, + /// (GifToVideo value) => {...}, + /// (Thumbnail value) => {...}, + /// (Abs value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action transformation, + System::Action gifToVideo, + System::Action thumbnail, + System::Action abs + ) + { + switch (this.Value) + { + case PostTransformation value: + transformation(value); + break; + case GifToVideo value: + gifToVideo(value); + break; + case Thumbnail value: + thumbnail(value); + break; + case Abs value: + abs(value); + break; + default: + throw new ImageKitInvalidDataException("Data did not match any variant of Post"); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (PostTransformation value) => {...}, + /// (GifToVideo value) => {...}, + /// (Thumbnail value) => {...}, + /// (Abs value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func transformation, + System::Func gifToVideo, + System::Func thumbnail, + System::Func abs + ) + { + return this.Value switch + { + PostTransformation value => transformation(value), + GifToVideo value => gifToVideo(value), + Thumbnail value => thumbnail(value), + Abs value => abs(value), + _ => throw new ImageKitInvalidDataException("Data did not match any variant of Post"), + }; + } + + public static implicit operator Post(PostTransformation value) => new(value); + + public static implicit operator Post(GifToVideo value) => new(value); + + public static implicit operator Post(Thumbnail value) => new(value); + + public static implicit operator Post(Abs value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of Post"); + } + this.Switch( + (transformation) => transformation.Validate(), + (gifToVideo) => gifToVideo.Validate(), + (thumbnail) => thumbnail.Validate(), + (abs) => abs.Validate() + ); + } + + public virtual bool Equals(Post? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + PostTransformation _ => 0, + GifToVideo _ => 1, + Thumbnail _ => 2, + Abs _ => 3, + _ => -1, + }; + } +} + +sealed class PostConverter : JsonConverter +{ + public override Post? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + string? type; + try + { + type = element.GetProperty("type").GetString(); + } + catch + { + type = null; + } + + switch (type) + { + case "transformation": + { + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "gif-to-video": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "thumbnail": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "abs": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + default: + { + return new Post(element); + } + } + } + + public override void Write(Utf8JsonWriter writer, Post value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class PostTransformation : JsonModel +{ + /// + /// Transformation type. + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Transformation string (e.g. `w-200,h-200`). Same syntax as ImageKit URL-based + /// transformations. + /// + public required string ValueValue + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("value"); + } + init { this._rawData.Set("value", value); } + } + + /// + public override void Validate() + { + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("transformation"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.ValueValue; + } + + public PostTransformation() + { + this.Type = JsonSerializer.SerializeToElement("transformation"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public PostTransformation(PostTransformation postTransformation) + : base(postTransformation) { } +#pragma warning restore CS8618 + + public PostTransformation(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("transformation"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + PostTransformation(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static PostTransformation FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public PostTransformation(string valueValue) + : this() + { + this.ValueValue = valueValue; + } +} + +class PostTransformationFromRaw : IFromRawJson +{ + /// + public PostTransformation FromRawUnchecked(IReadOnlyDictionary rawData) => + PostTransformation.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class GifToVideo : JsonModel +{ + /// + /// Converts an animated GIF into an MP4. + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Optional transformation string to apply to the output video. **Example**: + /// `q-80` + /// + public string? Value + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("value"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("value", value); + } + } + + /// + public override void Validate() + { + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("gif-to-video"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.Value; + } + + public GifToVideo() + { + this.Type = JsonSerializer.SerializeToElement("gif-to-video"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public GifToVideo(GifToVideo gifToVideo) + : base(gifToVideo) { } +#pragma warning restore CS8618 + + public GifToVideo(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("gif-to-video"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + GifToVideo(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static GifToVideo FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class GifToVideoFromRaw : IFromRawJson +{ + /// + public GifToVideo FromRawUnchecked(IReadOnlyDictionary rawData) => + GifToVideo.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Thumbnail : JsonModel +{ + /// + /// Generates a thumbnail image. + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Optional transformation string. **Example**: `w-150,h-150` + /// + public string? Value + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("value"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("value", value); + } + } + + /// + public override void Validate() + { + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("thumbnail"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.Value; + } + + public Thumbnail() + { + this.Type = JsonSerializer.SerializeToElement("thumbnail"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Thumbnail(Thumbnail thumbnail) + : base(thumbnail) { } +#pragma warning restore CS8618 + + public Thumbnail(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("thumbnail"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Thumbnail(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Thumbnail FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ThumbnailFromRaw : IFromRawJson +{ + /// + public Thumbnail FromRawUnchecked(IReadOnlyDictionary rawData) => + Thumbnail.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Abs : JsonModel +{ + /// + /// Streaming protocol to use (`hls` or `dash`). + /// + public required ApiEnum Protocol + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass>("protocol"); + } + init { this._rawData.Set("protocol", value); } + } + + /// + /// Adaptive Bitrate Streaming (ABS) setup. + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// List of different representations you want to create separated by an underscore. + /// + public required string Value + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("value"); + } + init { this._rawData.Set("value", value); } + } + + /// + public override void Validate() + { + this.Protocol.Validate(); + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("abs"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + _ = this.Value; + } + + public Abs() + { + this.Type = JsonSerializer.SerializeToElement("abs"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Abs(Abs abs) + : base(abs) { } +#pragma warning restore CS8618 + + public Abs(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("abs"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Abs(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Abs FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class AbsFromRaw : IFromRawJson +{ + /// + public Abs FromRawUnchecked(IReadOnlyDictionary rawData) => + Abs.FromRawUnchecked(rawData); +} + +/// +/// Streaming protocol to use (`hls` or `dash`). +/// +[JsonConverter(typeof(ProtocolConverter))] +public enum Protocol +{ + Hls, + Dash, +} + +sealed class ProtocolConverter : JsonConverter +{ + public override Protocol Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "hls" => Protocol.Hls, + "dash" => Protocol.Dash, + _ => (Protocol)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, Protocol value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + Protocol.Hls => "hls", + Protocol.Dash => "dash", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} diff --git a/src/Imagekit/Models/Files/FileUploadResponse.cs b/src/Imagekit/Models/Files/FileUploadResponse.cs new file mode 100644 index 00000000..156bff3a --- /dev/null +++ b/src/Imagekit/Models/Files/FileUploadResponse.cs @@ -0,0 +1,1062 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models.Files; + +/// +/// Object containing details of a successful upload. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class FileUploadResponse : JsonModel +{ + /// + /// An array of tags assigned to the uploaded file by auto tagging. + /// + public IReadOnlyList? AITags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("AITags"); + } + init + { + this._rawData.Set?>( + "AITags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// The audio codec used in the video (only for video). + /// + public string? AudioCodec + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("audioCodec"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("audioCodec", value); + } + } + + /// + /// The bit rate of the video in kbps (only for video). + /// + public long? BitRate + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("bitRate"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("bitRate", value); + } + } + + /// + /// Value of custom coordinates associated with the image in the format `x,y,width,height`. + /// If `customCoordinates` are not defined, then it is `null`. Send `customCoordinates` + /// in `responseFields` in API request to get the value of this field. + /// + public string? CustomCoordinates + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("customCoordinates"); + } + init { this._rawData.Set("customCoordinates", value); } + } + + /// + /// A key-value data associated with the asset. Use `responseField` in API request + /// to get `customMetadata` in the upload API response. Before setting any custom + /// metadata on an asset, you have to create the field using custom metadata fields + /// API. Send `customMetadata` in `responseFields` in API request to get the value + /// of this field. + /// + public IReadOnlyDictionary? CustomMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "customMetadata" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "customMetadata", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Optional text to describe the contents of the file. Can be set by the user + /// or the ai-auto-description extension. + /// + public string? Description + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("description"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("description", value); + } + } + + /// + /// The duration of the video in seconds (only for video). + /// + public long? Duration + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("duration"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("duration", value); + } + } + + /// + /// Consolidated embedded metadata associated with the file. It includes exif, + /// iptc, and xmp data. Send `embeddedMetadata` in `responseFields` in API request + /// to get embeddedMetadata in the upload API response. + /// + public IReadOnlyDictionary? EmbeddedMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "embeddedMetadata" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "embeddedMetadata", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Extension names with their processing status at the time of completion of + /// the request. It could have one of the following status values: + /// + /// `success`: The extension has been successfully applied. `failed`: The + /// extension has failed and will not be retried. `pending`: The extension will + /// finish processing in some time. On completion, the final status (success + /// / failed) will be sent to the `webhookUrl` provided. + /// + /// If no extension was requested, then this parameter is not returned. + /// + public FileUploadResponseExtensionStatus? ExtensionStatus + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass( + "extensionStatus" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("extensionStatus", value); + } + } + + /// + /// Unique fileId. Store this fileld in your database, as this will be used to + /// perform update action on this file. + /// + public string? FileID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("fileId"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("fileId", value); + } + } + + /// + /// The relative path of the file in the media library e.g. `/marketing-assets/new-banner.jpg`. + /// + public string? FilePath + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("filePath"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("filePath", value); + } + } + + /// + /// Type of the uploaded file. Possible values are `image`, `non-image`. + /// + public string? FileType + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("fileType"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("fileType", value); + } + } + + /// + /// Height of the image in pixels (Only for images) + /// + public double? Height + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("height"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("height", value); + } + } + + /// + /// Is the file marked as private. It can be either `true` or `false`. Send `isPrivateFile` + /// in `responseFields` in API request to get the value of this field. + /// + public bool? IsPrivateFile + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("isPrivateFile"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("isPrivateFile", value); + } + } + + /// + /// Is the file published or in draft state. It can be either `true` or `false`. + /// Send `isPublished` in `responseFields` in API request to get the value of + /// this field. + /// + public bool? IsPublished + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("isPublished"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("isPublished", value); + } + } + + /// + /// Legacy metadata. Send `metadata` in `responseFields` in API request to get + /// metadata in the upload API response. + /// + public FileMetadata? Metadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("metadata"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("metadata", value); + } + } + + /// + /// Name of the asset. + /// + public string? Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("name"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("name", value); + } + } + + /// + /// This field is included in the response only if the Path policy feature is + /// available in the plan. It contains schema definitions for the custom metadata + /// fields selected for the specified file path. Field selection can only be + /// done when the Path policy feature is enabled. + /// + /// Keys are the names of the custom metadata fields; the value object + /// has details about the custom metadata schema. + /// + public IReadOnlyDictionary? SelectedFieldsSchema + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + FrozenDictionary + >("selectedFieldsSchema"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "selectedFieldsSchema", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Size of the image file in Bytes. + /// + public double? Size + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("size"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("size", value); + } + } + + /// + /// The array of tags associated with the asset. If no tags are set, it will be + /// `null`. Send `tags` in `responseFields` in API request to get the value of + /// this field. + /// + public IReadOnlyList? Tags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("tags"); + } + init + { + this._rawData.Set?>( + "tags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// In the case of an image, a small thumbnail URL. + /// + public string? ThumbnailUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("thumbnailUrl"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("thumbnailUrl", value); + } + } + + /// + /// A publicly accessible URL of the file. + /// + public string? Url + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("url"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("url", value); + } + } + + /// + /// An object containing the file or file version's `id` (versionId) and `name`. + /// + public VersionInfo? VersionInfo + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("versionInfo"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("versionInfo", value); + } + } + + /// + /// The video codec used in the video (only for video). + /// + public string? VideoCodec + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("videoCodec"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("videoCodec", value); + } + } + + /// + /// Width of the image in pixels (Only for Images) + /// + public double? Width + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("width"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("width", value); + } + } + + /// + public override void Validate() + { + foreach (var item in this.AITags ?? []) + { + item.Validate(); + } + _ = this.AudioCodec; + _ = this.BitRate; + _ = this.CustomCoordinates; + _ = this.CustomMetadata; + _ = this.Description; + _ = this.Duration; + _ = this.EmbeddedMetadata; + this.ExtensionStatus?.Validate(); + _ = this.FileID; + _ = this.FilePath; + _ = this.FileType; + _ = this.Height; + _ = this.IsPrivateFile; + _ = this.IsPublished; + this.Metadata?.Validate(); + _ = this.Name; + if (this.SelectedFieldsSchema != null) + { + foreach (var item in this.SelectedFieldsSchema.Values) + { + item.Validate(); + } + } + _ = this.Size; + _ = this.Tags; + _ = this.ThumbnailUrl; + _ = this.Url; + this.VersionInfo?.Validate(); + _ = this.VideoCodec; + _ = this.Width; + } + + public FileUploadResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileUploadResponse(FileUploadResponse fileUploadResponse) + : base(fileUploadResponse) { } +#pragma warning restore CS8618 + + public FileUploadResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileUploadResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FileUploadResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FileUploadResponseFromRaw : IFromRawJson +{ + /// + public FileUploadResponse FromRawUnchecked(IReadOnlyDictionary rawData) => + FileUploadResponse.FromRawUnchecked(rawData); +} + +/// +/// Extension names with their processing status at the time of completion of the +/// request. It could have one of the following status values: +/// +/// `success`: The extension has been successfully applied. `failed`: The extension +/// has failed and will not be retried. `pending`: The extension will finish processing +/// in some time. On completion, the final status (success / failed) will be sent +/// to the `webhookUrl` provided. +/// +/// If no extension was requested, then this parameter is not returned. +/// +[JsonConverter( + typeof(JsonModelConverter< + FileUploadResponseExtensionStatus, + FileUploadResponseExtensionStatusFromRaw + >) +)] +public sealed record class FileUploadResponseExtensionStatus : JsonModel +{ + public ApiEnum? AIAutoDescription + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum + >("ai-auto-description"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ai-auto-description", value); + } + } + + public ApiEnum? AITasks + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum + >("ai-tasks"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ai-tasks", value); + } + } + + public ApiEnum? AwsAutoTagging + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum + >("aws-auto-tagging"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("aws-auto-tagging", value); + } + } + + public ApiEnum? GoogleAutoTagging + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum + >("google-auto-tagging"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("google-auto-tagging", value); + } + } + + public ApiEnum? RemoveBg + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum + >("remove-bg"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("remove-bg", value); + } + } + + /// + public override void Validate() + { + this.AIAutoDescription?.Validate(); + this.AITasks?.Validate(); + this.AwsAutoTagging?.Validate(); + this.GoogleAutoTagging?.Validate(); + this.RemoveBg?.Validate(); + } + + public FileUploadResponseExtensionStatus() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileUploadResponseExtensionStatus( + FileUploadResponseExtensionStatus fileUploadResponseExtensionStatus + ) + : base(fileUploadResponseExtensionStatus) { } +#pragma warning restore CS8618 + + public FileUploadResponseExtensionStatus(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileUploadResponseExtensionStatus(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FileUploadResponseExtensionStatus FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FileUploadResponseExtensionStatusFromRaw : IFromRawJson +{ + /// + public FileUploadResponseExtensionStatus FromRawUnchecked( + IReadOnlyDictionary rawData + ) => FileUploadResponseExtensionStatus.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(FileUploadResponseExtensionStatusAIAutoDescriptionConverter))] +public enum FileUploadResponseExtensionStatusAIAutoDescription +{ + Success, + Pending, + Failed, +} + +sealed class FileUploadResponseExtensionStatusAIAutoDescriptionConverter + : JsonConverter +{ + public override FileUploadResponseExtensionStatusAIAutoDescription Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "success" => FileUploadResponseExtensionStatusAIAutoDescription.Success, + "pending" => FileUploadResponseExtensionStatusAIAutoDescription.Pending, + "failed" => FileUploadResponseExtensionStatusAIAutoDescription.Failed, + _ => (FileUploadResponseExtensionStatusAIAutoDescription)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + FileUploadResponseExtensionStatusAIAutoDescription value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + FileUploadResponseExtensionStatusAIAutoDescription.Success => "success", + FileUploadResponseExtensionStatusAIAutoDescription.Pending => "pending", + FileUploadResponseExtensionStatusAIAutoDescription.Failed => "failed", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +[JsonConverter(typeof(FileUploadResponseExtensionStatusAITasksConverter))] +public enum FileUploadResponseExtensionStatusAITasks +{ + Success, + Pending, + Failed, +} + +sealed class FileUploadResponseExtensionStatusAITasksConverter + : JsonConverter +{ + public override FileUploadResponseExtensionStatusAITasks Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "success" => FileUploadResponseExtensionStatusAITasks.Success, + "pending" => FileUploadResponseExtensionStatusAITasks.Pending, + "failed" => FileUploadResponseExtensionStatusAITasks.Failed, + _ => (FileUploadResponseExtensionStatusAITasks)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + FileUploadResponseExtensionStatusAITasks value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + FileUploadResponseExtensionStatusAITasks.Success => "success", + FileUploadResponseExtensionStatusAITasks.Pending => "pending", + FileUploadResponseExtensionStatusAITasks.Failed => "failed", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +[JsonConverter(typeof(FileUploadResponseExtensionStatusAwsAutoTaggingConverter))] +public enum FileUploadResponseExtensionStatusAwsAutoTagging +{ + Success, + Pending, + Failed, +} + +sealed class FileUploadResponseExtensionStatusAwsAutoTaggingConverter + : JsonConverter +{ + public override FileUploadResponseExtensionStatusAwsAutoTagging Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "success" => FileUploadResponseExtensionStatusAwsAutoTagging.Success, + "pending" => FileUploadResponseExtensionStatusAwsAutoTagging.Pending, + "failed" => FileUploadResponseExtensionStatusAwsAutoTagging.Failed, + _ => (FileUploadResponseExtensionStatusAwsAutoTagging)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + FileUploadResponseExtensionStatusAwsAutoTagging value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + FileUploadResponseExtensionStatusAwsAutoTagging.Success => "success", + FileUploadResponseExtensionStatusAwsAutoTagging.Pending => "pending", + FileUploadResponseExtensionStatusAwsAutoTagging.Failed => "failed", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +[JsonConverter(typeof(FileUploadResponseExtensionStatusGoogleAutoTaggingConverter))] +public enum FileUploadResponseExtensionStatusGoogleAutoTagging +{ + Success, + Pending, + Failed, +} + +sealed class FileUploadResponseExtensionStatusGoogleAutoTaggingConverter + : JsonConverter +{ + public override FileUploadResponseExtensionStatusGoogleAutoTagging Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "success" => FileUploadResponseExtensionStatusGoogleAutoTagging.Success, + "pending" => FileUploadResponseExtensionStatusGoogleAutoTagging.Pending, + "failed" => FileUploadResponseExtensionStatusGoogleAutoTagging.Failed, + _ => (FileUploadResponseExtensionStatusGoogleAutoTagging)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + FileUploadResponseExtensionStatusGoogleAutoTagging value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + FileUploadResponseExtensionStatusGoogleAutoTagging.Success => "success", + FileUploadResponseExtensionStatusGoogleAutoTagging.Pending => "pending", + FileUploadResponseExtensionStatusGoogleAutoTagging.Failed => "failed", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +[JsonConverter(typeof(FileUploadResponseExtensionStatusRemoveBgConverter))] +public enum FileUploadResponseExtensionStatusRemoveBg +{ + Success, + Pending, + Failed, +} + +sealed class FileUploadResponseExtensionStatusRemoveBgConverter + : JsonConverter +{ + public override FileUploadResponseExtensionStatusRemoveBg Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "success" => FileUploadResponseExtensionStatusRemoveBg.Success, + "pending" => FileUploadResponseExtensionStatusRemoveBg.Pending, + "failed" => FileUploadResponseExtensionStatusRemoveBg.Failed, + _ => (FileUploadResponseExtensionStatusRemoveBg)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + FileUploadResponseExtensionStatusRemoveBg value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + FileUploadResponseExtensionStatusRemoveBg.Success => "success", + FileUploadResponseExtensionStatusRemoveBg.Pending => "pending", + FileUploadResponseExtensionStatusRemoveBg.Failed => "failed", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} diff --git a/src/Imagekit/Models/Files/Folder.cs b/src/Imagekit/Models/Files/Folder.cs new file mode 100644 index 00000000..a06eaf4d --- /dev/null +++ b/src/Imagekit/Models/Files/Folder.cs @@ -0,0 +1,262 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models.Files; + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Folder : JsonModel +{ + /// + /// Date and time when the folder was created. The date and time is in ISO8601 + /// format. + /// + public System::DateTimeOffset? CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("createdAt"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("createdAt", value); + } + } + + /// + /// An object with custom metadata for the folder. Returns empty object if no + /// custom metadata is set. + /// + public IReadOnlyDictionary? CustomMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "customMetadata" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "customMetadata", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Unique identifier of the asset. + /// + public string? FolderID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("folderId"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("folderId", value); + } + } + + /// + /// Path of the folder. This is the path you would use in the URL to access the + /// folder. For example, if the folder is at the root of the media library, the + /// path will be /folder. If the folder is inside another folder named images, + /// the path will be /images/folder. + /// + public string? FolderPath + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("folderPath"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("folderPath", value); + } + } + + /// + /// Name of the asset. + /// + public string? Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("name"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("name", value); + } + } + + /// + /// Type of the asset. + /// + public ApiEnum? Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("type"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("type", value); + } + } + + /// + /// Date and time when the folder was last updated. The date and time is in ISO8601 + /// format. + /// + public System::DateTimeOffset? UpdatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("updatedAt"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("updatedAt", value); + } + } + + /// + public override void Validate() + { + _ = this.CreatedAt; + _ = this.CustomMetadata; + _ = this.FolderID; + _ = this.FolderPath; + _ = this.Name; + this.Type?.Validate(); + _ = this.UpdatedAt; + } + + public Folder() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Folder(Folder folder) + : base(folder) { } +#pragma warning restore CS8618 + + public Folder(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Folder(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Folder FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FolderFromRaw : IFromRawJson +{ + /// + public Folder FromRawUnchecked(IReadOnlyDictionary rawData) => + Folder.FromRawUnchecked(rawData); +} + +/// +/// Type of the asset. +/// +[JsonConverter(typeof(FolderTypeConverter))] +public enum FolderType +{ + Folder, +} + +sealed class FolderTypeConverter : JsonConverter +{ + public override FolderType Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "folder" => FolderType.Folder, + _ => (FolderType)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + FolderType value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + FolderType.Folder => "folder", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} diff --git a/src/Imagekit/Models/Files/Metadata/MetadataGetFromUrlParams.cs b/src/Imagekit/Models/Files/Metadata/MetadataGetFromUrlParams.cs new file mode 100644 index 00000000..b1823fc5 --- /dev/null +++ b/src/Imagekit/Models/Files/Metadata/MetadataGetFromUrlParams.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Models.Files.Metadata; + +/// +/// Get image EXIF, pHash, and other metadata from ImageKit.io powered remote URL +/// using this API. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class MetadataGetFromUrlParams : ParamsBase +{ + /// + /// Should be a valid file URL. It should be accessible using your ImageKit.io + /// account. + /// + public required string UrlValue + { + get + { + this._rawQueryData.Freeze(); + return this._rawQueryData.GetNotNullClass("url"); + } + init { this._rawQueryData.Set("url", value); } + } + + public MetadataGetFromUrlParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public MetadataGetFromUrlParams(MetadataGetFromUrlParams metadataGetFromUrlParams) + : base(metadataGetFromUrlParams) { } +#pragma warning restore CS8618 + + public MetadataGetFromUrlParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + MetadataGetFromUrlParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } +#pragma warning restore CS8618 + + /// + public static MetadataGetFromUrlParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(MetadataGetFromUrlParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder(options.BaseUrl.ToString().TrimEnd('/') + "/v1/metadata") + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Files/Metadata/MetadataGetParams.cs b/src/Imagekit/Models/Files/Metadata/MetadataGetParams.cs new file mode 100644 index 00000000..93a4f7de --- /dev/null +++ b/src/Imagekit/Models/Files/Metadata/MetadataGetParams.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Models.Files.Metadata; + +/// +/// You can programmatically get image EXIF, pHash, and other metadata for uploaded +/// files in the ImageKit.io media library using this API. +/// +/// You can also get the metadata in upload API response by passing `metadata` +/// in `responseFields` parameter. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class MetadataGetParams : ParamsBase +{ + public string? FileID { get; init; } + + public MetadataGetParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public MetadataGetParams(MetadataGetParams metadataGetParams) + : base(metadataGetParams) + { + this.FileID = metadataGetParams.FileID; + } +#pragma warning restore CS8618 + + public MetadataGetParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + MetadataGetParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + string fileID + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this.FileID = fileID; + } +#pragma warning restore CS8618 + + /// + public static MetadataGetParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + string fileID + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + fileID + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["FileID"] = JsonSerializer.SerializeToElement(this.FileID), + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(MetadataGetParams? other) + { + if (other == null) + { + return false; + } + return (this.FileID?.Equals(other.FileID) ?? other.FileID == null) + && this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + + string.Format("/v1/files/{0}/metadata", this.FileID) + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Files/UpdateFileRequest.cs b/src/Imagekit/Models/Files/UpdateFileRequest.cs new file mode 100644 index 00000000..a56ec874 --- /dev/null +++ b/src/Imagekit/Models/Files/UpdateFileRequest.cs @@ -0,0 +1,973 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models.Files; + +/// +/// Schema for update file update request. +/// +[JsonConverter(typeof(UpdateFileRequestConverter))] +public record class UpdateFileRequest : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public UpdateFileRequest(UpdateFileDetails value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UpdateFileRequest(ChangePublicationStatus value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UpdateFileRequest(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDetails(out var value)) { + /// // `value` is of type `UpdateFileDetails` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDetails([NotNullWhen(true)] out UpdateFileDetails? value) + { + value = this.Value as UpdateFileDetails; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickChangePublicationStatus(out var value)) { + /// // `value` is of type `ChangePublicationStatus` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickChangePublicationStatus( + [NotNullWhen(true)] out ChangePublicationStatus? value + ) + { + value = this.Value as ChangePublicationStatus; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (UpdateFileDetails value) => {...}, + /// (ChangePublicationStatus value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action details, + System::Action changePublicationStatus + ) + { + switch (this.Value) + { + case UpdateFileDetails value: + details(value); + break; + case ChangePublicationStatus value: + changePublicationStatus(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of UpdateFileRequest" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (UpdateFileDetails value) => {...}, + /// (ChangePublicationStatus value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func details, + System::Func changePublicationStatus + ) + { + return this.Value switch + { + UpdateFileDetails value => details(value), + ChangePublicationStatus value => changePublicationStatus(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of UpdateFileRequest" + ), + }; + } + + public static implicit operator UpdateFileRequest(UpdateFileDetails value) => new(value); + + public static implicit operator UpdateFileRequest(ChangePublicationStatus value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of UpdateFileRequest" + ); + } + this.Switch( + (details) => details.Validate(), + (changePublicationStatus) => changePublicationStatus.Validate() + ); + } + + public virtual bool Equals(UpdateFileRequest? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + UpdateFileDetails _ => 0, + ChangePublicationStatus _ => 1, + _ => -1, + }; + } +} + +sealed class UpdateFileRequestConverter : JsonConverter +{ + public override UpdateFileRequest? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + UpdateFileRequest value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class UpdateFileDetails : JsonModel +{ + /// + /// Define an important area in the image in the format `x,y,width,height` e.g. + /// `10,10,100,100`. Send `null` to unset this value. + /// + public string? CustomCoordinates + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("customCoordinates"); + } + init { this._rawData.Set("customCoordinates", value); } + } + + /// + /// A key-value data to be associated with the asset. To unset a key, send `null` + /// value for that key. Before setting any custom metadata on an asset you have + /// to create the field using custom metadata fields API. + /// + public IReadOnlyDictionary? CustomMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "customMetadata" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "customMetadata", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Optional text to describe the contents of the file. + /// + public string? Description + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("description"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("description", value); + } + } + + /// + /// Array of extensions to be applied to the asset. Each extension can be configured + /// with specific parameters based on the extension type. + /// + public IReadOnlyList? Extensions + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("extensions"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "extensions", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// An array of AITags associated with the file that you want to remove, e.g. + /// `["car", "vehicle", "motorsports"]`. + /// + /// If you want to remove all AITags associated with the file, send a string + /// - "all". + /// + /// Note: The remove operation for `AITags` executes before any of the `extensions` + /// are processed. + /// + public RemoveAITags? RemoveAITags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("removeAITags"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("removeAITags", value); + } + } + + /// + /// An array of tags associated with the file, such as `["tag1", "tag2"]`. Send + /// `null` to unset all tags associated with the file. + /// + public IReadOnlyList? Tags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("tags"); + } + init + { + this._rawData.Set?>( + "tags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// The final status of extensions after they have completed execution will be + /// delivered to this endpoint as a POST request. [Learn more](/docs/api-reference/digital-asset-management-dam/managing-assets/update-file-details#webhook-payload-structure) + /// about the webhook payload structure. + /// + public string? WebhookUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("webhookUrl"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("webhookUrl", value); + } + } + + /// + public override void Validate() + { + _ = this.CustomCoordinates; + _ = this.CustomMetadata; + _ = this.Description; + foreach (var item in this.Extensions ?? []) + { + item.Validate(); + } + this.RemoveAITags?.Validate(); + _ = this.Tags; + _ = this.WebhookUrl; + } + + public UpdateFileDetails() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UpdateFileDetails(UpdateFileDetails updateFileDetails) + : base(updateFileDetails) { } +#pragma warning restore CS8618 + + public UpdateFileDetails(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UpdateFileDetails(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UpdateFileDetails FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class UpdateFileDetailsFromRaw : IFromRawJson +{ + /// + public UpdateFileDetails FromRawUnchecked(IReadOnlyDictionary rawData) => + UpdateFileDetails.FromRawUnchecked(rawData); +} + +/// +/// An array of AITags associated with the file that you want to remove, e.g. `["car", +/// "vehicle", "motorsports"]`. +/// +/// If you want to remove all AITags associated with the file, send a string +/// - "all". +/// +/// Note: The remove operation for `AITags` executes before any of the `extensions` +/// are processed. +/// +[JsonConverter(typeof(RemoveAITagsConverter))] +public record class RemoveAITags : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public RemoveAITags(IReadOnlyList value, JsonElement? element = null) + { + this.Value = ImmutableArray.ToImmutableArray(value); + this._element = element; + } + + public RemoveAITags(All value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public RemoveAITags(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type where T is a string. + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickStrings(out var value)) { + /// // `value` is of type `IReadOnlyList<string>` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickStrings([NotNullWhen(true)] out IReadOnlyList? value) + { + value = this.Value as IReadOnlyList; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickAll(out var value)) { + /// // `value` is of type `All` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickAll([NotNullWhen(true)] out All? value) + { + value = this.Value as All; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (IReadOnlyList<string> value) => {...}, + /// (All value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action> strings, System::Action all) + { + switch (this.Value) + { + case IReadOnlyList value: + strings(value); + break; + case All value: + all(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of RemoveAITags" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (IReadOnlyList<string> value) => {...}, + /// (All value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func, T> strings, System::Func all) + { + return this.Value switch + { + IReadOnlyList value => strings(value), + All value => all(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of RemoveAITags" + ), + }; + } + + public static implicit operator RemoveAITags(List value) => + new((IReadOnlyList)value); + + public static implicit operator RemoveAITags(All value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of RemoveAITags" + ); + } + this.Switch((_) => { }, (all) => all.Validate()); + } + + public virtual bool Equals(RemoveAITags? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + IReadOnlyList _ => 0, + All _ => 1, + _ => -1, + }; + } +} + +sealed class RemoveAITagsConverter : JsonConverter +{ + public override RemoveAITags? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize>(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + RemoveAITags value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(AllConverter))] +public record class All +{ + public JsonElement Element { get; private init; } + + public All() + { + Element = JsonSerializer.SerializeToElement("all"); + } + + internal All(JsonElement element) + { + Element = element; + } + + /// + /// Validates that the instance's underlying value is the expected constant. + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public void Validate() + { + if (this != new All()) + { + throw new ImageKitInvalidDataException("Invalid value given for 'All'"); + } + } + + public override int GetHashCode() + { + return 0; + } + + public virtual bool Equals(All? other) + { + if (other == null) + { + return false; + } + + return JsonElement.DeepEquals(this.Element, other.Element); + } +} + +class AllConverter : JsonConverter +{ + public override All? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return new(JsonSerializer.Deserialize(ref reader, options)); + } + + public override void Write(Utf8JsonWriter writer, All value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Element, options); + } +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class ChangePublicationStatus : JsonModel +{ + /// + /// Configure the publication status of a file and its versions. + /// + public Publish? Publish + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("publish"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("publish", value); + } + } + + /// + public override void Validate() + { + this.Publish?.Validate(); + } + + public ChangePublicationStatus() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ChangePublicationStatus(ChangePublicationStatus changePublicationStatus) + : base(changePublicationStatus) { } +#pragma warning restore CS8618 + + public ChangePublicationStatus(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ChangePublicationStatus(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ChangePublicationStatus FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ChangePublicationStatusFromRaw : IFromRawJson +{ + /// + public ChangePublicationStatus FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ChangePublicationStatus.FromRawUnchecked(rawData); +} + +/// +/// Configure the publication status of a file and its versions. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Publish : JsonModel +{ + /// + /// Set to `true` to publish the file. Set to `false` to unpublish the file. + /// + public required bool IsPublished + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("isPublished"); + } + init { this._rawData.Set("isPublished", value); } + } + + /// + /// Set to `true` to publish/unpublish all versions of the file. Set to `false` + /// to publish/unpublish only the current version of the file. + /// + public bool? IncludeFileVersions + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("includeFileVersions"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("includeFileVersions", value); + } + } + + /// + public override void Validate() + { + _ = this.IsPublished; + _ = this.IncludeFileVersions; + } + + public Publish() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Publish(Publish publish) + : base(publish) { } +#pragma warning restore CS8618 + + public Publish(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Publish(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Publish FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public Publish(bool isPublished) + : this() + { + this.IsPublished = isPublished; + } +} + +class PublishFromRaw : IFromRawJson +{ + /// + public Publish FromRawUnchecked(IReadOnlyDictionary rawData) => + Publish.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Files/Versions/VersionDeleteParams.cs b/src/Imagekit/Models/Files/Versions/VersionDeleteParams.cs new file mode 100644 index 00000000..8c600393 --- /dev/null +++ b/src/Imagekit/Models/Files/Versions/VersionDeleteParams.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Models.Files.Versions; + +/// +/// This API deletes a non-current file version permanently. The API returns an empty response. +/// +/// Note: If you want to delete all versions of a file, use the delete file API. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class VersionDeleteParams : ParamsBase +{ + public required string FileID { get; init; } + + public string? VersionID { get; init; } + + public VersionDeleteParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VersionDeleteParams(VersionDeleteParams versionDeleteParams) + : base(versionDeleteParams) + { + this.FileID = versionDeleteParams.FileID; + this.VersionID = versionDeleteParams.VersionID; + } +#pragma warning restore CS8618 + + public VersionDeleteParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VersionDeleteParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + string fileID, + string versionID + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this.FileID = fileID; + this.VersionID = versionID; + } +#pragma warning restore CS8618 + + /// + public static VersionDeleteParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + string fileID, + string versionID + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + fileID, + versionID + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["FileID"] = JsonSerializer.SerializeToElement(this.FileID), + ["VersionID"] = JsonSerializer.SerializeToElement(this.VersionID), + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(VersionDeleteParams? other) + { + if (other == null) + { + return false; + } + return this.FileID.Equals(other.FileID) + && (this.VersionID?.Equals(other.VersionID) ?? other.VersionID == null) + && this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + + string.Format("/v1/files/{0}/versions/{1}", this.FileID, this.VersionID) + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Files/Versions/VersionDeleteResponse.cs b/src/Imagekit/Models/Files/Versions/VersionDeleteResponse.cs new file mode 100644 index 00000000..6eef4640 --- /dev/null +++ b/src/Imagekit/Models/Files/Versions/VersionDeleteResponse.cs @@ -0,0 +1,52 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models.Files.Versions; + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class VersionDeleteResponse : JsonModel +{ + /// + public override void Validate() { } + + public VersionDeleteResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VersionDeleteResponse(VersionDeleteResponse versionDeleteResponse) + : base(versionDeleteResponse) { } +#pragma warning restore CS8618 + + public VersionDeleteResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VersionDeleteResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VersionDeleteResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class VersionDeleteResponseFromRaw : IFromRawJson +{ + /// + public VersionDeleteResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) => VersionDeleteResponse.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Files/Versions/VersionGetParams.cs b/src/Imagekit/Models/Files/Versions/VersionGetParams.cs new file mode 100644 index 00000000..0f4ca1bf --- /dev/null +++ b/src/Imagekit/Models/Files/Versions/VersionGetParams.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Models.Files.Versions; + +/// +/// This API returns an object with details or attributes of a file version. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class VersionGetParams : ParamsBase +{ + public required string FileID { get; init; } + + public string? VersionID { get; init; } + + public VersionGetParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VersionGetParams(VersionGetParams versionGetParams) + : base(versionGetParams) + { + this.FileID = versionGetParams.FileID; + this.VersionID = versionGetParams.VersionID; + } +#pragma warning restore CS8618 + + public VersionGetParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VersionGetParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + string fileID, + string versionID + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this.FileID = fileID; + this.VersionID = versionID; + } +#pragma warning restore CS8618 + + /// + public static VersionGetParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + string fileID, + string versionID + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + fileID, + versionID + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["FileID"] = JsonSerializer.SerializeToElement(this.FileID), + ["VersionID"] = JsonSerializer.SerializeToElement(this.VersionID), + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(VersionGetParams? other) + { + if (other == null) + { + return false; + } + return this.FileID.Equals(other.FileID) + && (this.VersionID?.Equals(other.VersionID) ?? other.VersionID == null) + && this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + + string.Format("/v1/files/{0}/versions/{1}", this.FileID, this.VersionID) + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Files/Versions/VersionListParams.cs b/src/Imagekit/Models/Files/Versions/VersionListParams.cs new file mode 100644 index 00000000..ecb7ac7f --- /dev/null +++ b/src/Imagekit/Models/Files/Versions/VersionListParams.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Models.Files.Versions; + +/// +/// This API returns details of all versions of a file. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class VersionListParams : ParamsBase +{ + public string? FileID { get; init; } + + public VersionListParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VersionListParams(VersionListParams versionListParams) + : base(versionListParams) + { + this.FileID = versionListParams.FileID; + } +#pragma warning restore CS8618 + + public VersionListParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VersionListParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + string fileID + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this.FileID = fileID; + } +#pragma warning restore CS8618 + + /// + public static VersionListParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + string fileID + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + fileID + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["FileID"] = JsonSerializer.SerializeToElement(this.FileID), + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(VersionListParams? other) + { + if (other == null) + { + return false; + } + return (this.FileID?.Equals(other.FileID) ?? other.FileID == null) + && this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + + string.Format("/v1/files/{0}/versions", this.FileID) + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Files/Versions/VersionRestoreParams.cs b/src/Imagekit/Models/Files/Versions/VersionRestoreParams.cs new file mode 100644 index 00000000..b12d3c89 --- /dev/null +++ b/src/Imagekit/Models/Files/Versions/VersionRestoreParams.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Models.Files.Versions; + +/// +/// This API restores a file version as the current file version. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class VersionRestoreParams : ParamsBase +{ + public required string FileID { get; init; } + + public string? VersionID { get; init; } + + public VersionRestoreParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VersionRestoreParams(VersionRestoreParams versionRestoreParams) + : base(versionRestoreParams) + { + this.FileID = versionRestoreParams.FileID; + this.VersionID = versionRestoreParams.VersionID; + } +#pragma warning restore CS8618 + + public VersionRestoreParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VersionRestoreParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + string fileID, + string versionID + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this.FileID = fileID; + this.VersionID = versionID; + } +#pragma warning restore CS8618 + + /// + public static VersionRestoreParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + string fileID, + string versionID + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + fileID, + versionID + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["FileID"] = JsonSerializer.SerializeToElement(this.FileID), + ["VersionID"] = JsonSerializer.SerializeToElement(this.VersionID), + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(VersionRestoreParams? other) + { + if (other == null) + { + return false; + } + return this.FileID.Equals(other.FileID) + && (this.VersionID?.Equals(other.VersionID) ?? other.VersionID == null) + && this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + + string.Format("/v1/files/{0}/versions/{1}/restore", this.FileID, this.VersionID) + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Folders/FolderCopyParams.cs b/src/Imagekit/Models/Folders/FolderCopyParams.cs new file mode 100644 index 00000000..54fee7eb --- /dev/null +++ b/src/Imagekit/Models/Folders/FolderCopyParams.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; +using Text = System.Text; + +namespace Imagekit.Models.Folders; + +/// +/// This will copy one folder into another. The selected folder, its nested folders, +/// files, and their versions (in `includeVersions` is set to true) are copied in +/// this operation. Note: If any file at the destination has the same name as the +/// source file, then the source file and its versions will be appended to the destination +/// file version history. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class FolderCopyParams : ParamsBase +{ + readonly JsonDictionary _rawBodyData = new(); + public IReadOnlyDictionary RawBodyData + { + get { return this._rawBodyData.Freeze(); } + } + + /// + /// Full path to the destination folder where you want to copy the source folder + /// into. + /// + public required string DestinationPath + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("destinationPath"); + } + init { this._rawBodyData.Set("destinationPath", value); } + } + + /// + /// The full path to the source folder you want to copy. + /// + public required string SourceFolderPath + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("sourceFolderPath"); + } + init { this._rawBodyData.Set("sourceFolderPath", value); } + } + + /// + /// Option to copy all versions of files that are nested inside the selected folder. + /// By default, only the current version of each file will be copied. When set + /// to true, all versions of each file will be copied. Default value - `false`. + /// + public bool? IncludeVersions + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct("includeVersions"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("includeVersions", value); + } + } + + public FolderCopyParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FolderCopyParams(FolderCopyParams folderCopyParams) + : base(folderCopyParams) + { + this._rawBodyData = new(folderCopyParams._rawBodyData); + } +#pragma warning restore CS8618 + + public FolderCopyParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FolderCopyParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + FrozenDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } +#pragma warning restore CS8618 + + /// + public static FolderCopyParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + FrozenDictionary.ToFrozenDictionary(rawBodyData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this._rawBodyData.Freeze()), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(FolderCopyParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this._rawBodyData.Equals(other._rawBodyData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder(options.BaseUrl.ToString().TrimEnd('/') + "/v1/bulkJobs/copyFolder") + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Folders/FolderCopyResponse.cs b/src/Imagekit/Models/Folders/FolderCopyResponse.cs new file mode 100644 index 00000000..a8d78bf5 --- /dev/null +++ b/src/Imagekit/Models/Folders/FolderCopyResponse.cs @@ -0,0 +1,78 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models.Folders; + +/// +/// Job submitted successfully. A `jobId` will be returned. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class FolderCopyResponse : JsonModel +{ + /// + /// Unique identifier of the bulk job. This can be used to check the status of + /// the bulk job. + /// + public required string JobID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("jobId"); + } + init { this._rawData.Set("jobId", value); } + } + + /// + public override void Validate() + { + _ = this.JobID; + } + + public FolderCopyResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FolderCopyResponse(FolderCopyResponse folderCopyResponse) + : base(folderCopyResponse) { } +#pragma warning restore CS8618 + + public FolderCopyResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FolderCopyResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FolderCopyResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public FolderCopyResponse(string jobID) + : this() + { + this.JobID = jobID; + } +} + +class FolderCopyResponseFromRaw : IFromRawJson +{ + /// + public FolderCopyResponse FromRawUnchecked(IReadOnlyDictionary rawData) => + FolderCopyResponse.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Folders/FolderCreateParams.cs b/src/Imagekit/Models/Folders/FolderCreateParams.cs new file mode 100644 index 00000000..43003cbc --- /dev/null +++ b/src/Imagekit/Models/Folders/FolderCreateParams.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; +using Text = System.Text; + +namespace Imagekit.Models.Folders; + +/// +/// This will create a new folder. You can specify the folder name and location of +/// the parent folder where this new folder should be created. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class FolderCreateParams : ParamsBase +{ + readonly JsonDictionary _rawBodyData = new(); + public IReadOnlyDictionary RawBodyData + { + get { return this._rawBodyData.Freeze(); } + } + + /// + /// The folder will be created with this name. + /// + /// All characters except alphabets and numbers (inclusive of unicode letters, + /// marks, and numerals in other languages) will be replaced by an underscore + /// i.e. `_`. + /// + public required string FolderName + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("folderName"); + } + init { this._rawBodyData.Set("folderName", value); } + } + + /// + /// The folder where the new folder should be created, for root use `/` else + /// the path e.g. `containing/folder/`. + /// + /// Note: If any folder(s) is not present in the parentFolderPath parameter, + /// it will be automatically created. For example, if you pass `/product/images/summer`, + /// then `product`, `images`, and `summer` folders will be created if they don't + /// already exist. + /// + public required string ParentFolderPath + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("parentFolderPath"); + } + init { this._rawBodyData.Set("parentFolderPath", value); } + } + + public FolderCreateParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FolderCreateParams(FolderCreateParams folderCreateParams) + : base(folderCreateParams) + { + this._rawBodyData = new(folderCreateParams._rawBodyData); + } +#pragma warning restore CS8618 + + public FolderCreateParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FolderCreateParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + FrozenDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } +#pragma warning restore CS8618 + + /// + public static FolderCreateParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + FrozenDictionary.ToFrozenDictionary(rawBodyData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this._rawBodyData.Freeze()), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(FolderCreateParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this._rawBodyData.Equals(other._rawBodyData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder(options.BaseUrl.ToString().TrimEnd('/') + "/v1/folder") + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Folders/FolderCreateResponse.cs b/src/Imagekit/Models/Folders/FolderCreateResponse.cs new file mode 100644 index 00000000..ccbd1f77 --- /dev/null +++ b/src/Imagekit/Models/Folders/FolderCreateResponse.cs @@ -0,0 +1,52 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models.Folders; + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class FolderCreateResponse : JsonModel +{ + /// + public override void Validate() { } + + public FolderCreateResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FolderCreateResponse(FolderCreateResponse folderCreateResponse) + : base(folderCreateResponse) { } +#pragma warning restore CS8618 + + public FolderCreateResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FolderCreateResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FolderCreateResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FolderCreateResponseFromRaw : IFromRawJson +{ + /// + public FolderCreateResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) => FolderCreateResponse.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Folders/FolderDeleteParams.cs b/src/Imagekit/Models/Folders/FolderDeleteParams.cs new file mode 100644 index 00000000..31faef01 --- /dev/null +++ b/src/Imagekit/Models/Folders/FolderDeleteParams.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; +using Text = System.Text; + +namespace Imagekit.Models.Folders; + +/// +/// This will delete a folder and all its contents permanently. The API returns an +/// empty response. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class FolderDeleteParams : ParamsBase +{ + readonly JsonDictionary _rawBodyData = new(); + public IReadOnlyDictionary RawBodyData + { + get { return this._rawBodyData.Freeze(); } + } + + /// + /// Full path to the folder you want to delete. For example `/folder/to/delete/`. + /// + public required string FolderPath + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("folderPath"); + } + init { this._rawBodyData.Set("folderPath", value); } + } + + public FolderDeleteParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FolderDeleteParams(FolderDeleteParams folderDeleteParams) + : base(folderDeleteParams) + { + this._rawBodyData = new(folderDeleteParams._rawBodyData); + } +#pragma warning restore CS8618 + + public FolderDeleteParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FolderDeleteParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + FrozenDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } +#pragma warning restore CS8618 + + /// + public static FolderDeleteParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + FrozenDictionary.ToFrozenDictionary(rawBodyData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this._rawBodyData.Freeze()), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(FolderDeleteParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this._rawBodyData.Equals(other._rawBodyData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder(options.BaseUrl.ToString().TrimEnd('/') + "/v1/folder") + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Folders/FolderDeleteResponse.cs b/src/Imagekit/Models/Folders/FolderDeleteResponse.cs new file mode 100644 index 00000000..14315872 --- /dev/null +++ b/src/Imagekit/Models/Folders/FolderDeleteResponse.cs @@ -0,0 +1,52 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models.Folders; + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class FolderDeleteResponse : JsonModel +{ + /// + public override void Validate() { } + + public FolderDeleteResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FolderDeleteResponse(FolderDeleteResponse folderDeleteResponse) + : base(folderDeleteResponse) { } +#pragma warning restore CS8618 + + public FolderDeleteResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FolderDeleteResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FolderDeleteResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FolderDeleteResponseFromRaw : IFromRawJson +{ + /// + public FolderDeleteResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) => FolderDeleteResponse.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Folders/FolderMoveParams.cs b/src/Imagekit/Models/Folders/FolderMoveParams.cs new file mode 100644 index 00000000..8d65ada2 --- /dev/null +++ b/src/Imagekit/Models/Folders/FolderMoveParams.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; +using Text = System.Text; + +namespace Imagekit.Models.Folders; + +/// +/// This will move one folder into another. The selected folder, its nested folders, +/// files, and their versions are moved in this operation. Note: If any file at the +/// destination has the same name as the source file, then the source file and its +/// versions will be appended to the destination file version history. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class FolderMoveParams : ParamsBase +{ + readonly JsonDictionary _rawBodyData = new(); + public IReadOnlyDictionary RawBodyData + { + get { return this._rawBodyData.Freeze(); } + } + + /// + /// Full path to the destination folder where you want to move the source folder + /// into. + /// + public required string DestinationPath + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("destinationPath"); + } + init { this._rawBodyData.Set("destinationPath", value); } + } + + /// + /// The full path to the source folder you want to move. + /// + public required string SourceFolderPath + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("sourceFolderPath"); + } + init { this._rawBodyData.Set("sourceFolderPath", value); } + } + + public FolderMoveParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FolderMoveParams(FolderMoveParams folderMoveParams) + : base(folderMoveParams) + { + this._rawBodyData = new(folderMoveParams._rawBodyData); + } +#pragma warning restore CS8618 + + public FolderMoveParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FolderMoveParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + FrozenDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } +#pragma warning restore CS8618 + + /// + public static FolderMoveParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + FrozenDictionary.ToFrozenDictionary(rawBodyData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this._rawBodyData.Freeze()), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(FolderMoveParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this._rawBodyData.Equals(other._rawBodyData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder(options.BaseUrl.ToString().TrimEnd('/') + "/v1/bulkJobs/moveFolder") + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Folders/FolderMoveResponse.cs b/src/Imagekit/Models/Folders/FolderMoveResponse.cs new file mode 100644 index 00000000..b4826761 --- /dev/null +++ b/src/Imagekit/Models/Folders/FolderMoveResponse.cs @@ -0,0 +1,78 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models.Folders; + +/// +/// Job submitted successfully. A `jobId` will be returned. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class FolderMoveResponse : JsonModel +{ + /// + /// Unique identifier of the bulk job. This can be used to check the status of + /// the bulk job. + /// + public required string JobID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("jobId"); + } + init { this._rawData.Set("jobId", value); } + } + + /// + public override void Validate() + { + _ = this.JobID; + } + + public FolderMoveResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FolderMoveResponse(FolderMoveResponse folderMoveResponse) + : base(folderMoveResponse) { } +#pragma warning restore CS8618 + + public FolderMoveResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FolderMoveResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FolderMoveResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public FolderMoveResponse(string jobID) + : this() + { + this.JobID = jobID; + } +} + +class FolderMoveResponseFromRaw : IFromRawJson +{ + /// + public FolderMoveResponse FromRawUnchecked(IReadOnlyDictionary rawData) => + FolderMoveResponse.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Folders/FolderRenameParams.cs b/src/Imagekit/Models/Folders/FolderRenameParams.cs new file mode 100644 index 00000000..52180b32 --- /dev/null +++ b/src/Imagekit/Models/Folders/FolderRenameParams.cs @@ -0,0 +1,202 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; +using Text = System.Text; + +namespace Imagekit.Models.Folders; + +/// +/// This API allows you to rename an existing folder. The folder and all its nested +/// assets and sub-folders will remain unchanged, but their paths will be updated +/// to reflect the new folder name. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class FolderRenameParams : ParamsBase +{ + readonly JsonDictionary _rawBodyData = new(); + public IReadOnlyDictionary RawBodyData + { + get { return this._rawBodyData.Freeze(); } + } + + /// + /// The full path to the folder you want to rename. + /// + public required string FolderPath + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("folderPath"); + } + init { this._rawBodyData.Set("folderPath", value); } + } + + /// + /// The new name for the folder. + /// + /// All characters except alphabets and numbers (inclusive of unicode letters, + /// marks, and numerals in other languages) and `-` will be replaced by an underscore + /// i.e. `_`. + /// + public required string NewFolderName + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("newFolderName"); + } + init { this._rawBodyData.Set("newFolderName", value); } + } + + /// + /// Option to purge cache for the old nested files and their versions' URLs. + /// + /// When set to true, it will internally issue a purge cache request on + /// CDN to remove the cached content of the old nested files and their versions. + /// There will only be one purge request for all the nested files, which will + /// be counted against your monthly purge quota. + /// + /// Note: A purge cache request will be issued against `https://ik.imagekit.io/old/folder/path*` + /// (with a wildcard at the end). This will remove all nested files, their versions' + /// URLs, and any transformations made using query parameters on these files or + /// their versions. However, the cache for file transformations made using path + /// parameters will persist. You can purge them using the purge API. For more + /// details, refer to the purge API documentation. + /// + /// Default value - `false` + /// + public bool? PurgeCache + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct("purgeCache"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("purgeCache", value); + } + } + + public FolderRenameParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FolderRenameParams(FolderRenameParams folderRenameParams) + : base(folderRenameParams) + { + this._rawBodyData = new(folderRenameParams._rawBodyData); + } +#pragma warning restore CS8618 + + public FolderRenameParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FolderRenameParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + FrozenDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } +#pragma warning restore CS8618 + + /// + public static FolderRenameParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + FrozenDictionary.ToFrozenDictionary(rawBodyData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this._rawBodyData.Freeze()), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(FolderRenameParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this._rawBodyData.Equals(other._rawBodyData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder(options.BaseUrl.ToString().TrimEnd('/') + "/v1/bulkJobs/renameFolder") + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Folders/FolderRenameResponse.cs b/src/Imagekit/Models/Folders/FolderRenameResponse.cs new file mode 100644 index 00000000..c330a60a --- /dev/null +++ b/src/Imagekit/Models/Folders/FolderRenameResponse.cs @@ -0,0 +1,79 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models.Folders; + +/// +/// Job submitted successfully. A `jobId` will be returned. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class FolderRenameResponse : JsonModel +{ + /// + /// Unique identifier of the bulk job. This can be used to check the status of + /// the bulk job. + /// + public required string JobID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("jobId"); + } + init { this._rawData.Set("jobId", value); } + } + + /// + public override void Validate() + { + _ = this.JobID; + } + + public FolderRenameResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FolderRenameResponse(FolderRenameResponse folderRenameResponse) + : base(folderRenameResponse) { } +#pragma warning restore CS8618 + + public FolderRenameResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FolderRenameResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FolderRenameResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public FolderRenameResponse(string jobID) + : this() + { + this.JobID = jobID; + } +} + +class FolderRenameResponseFromRaw : IFromRawJson +{ + /// + public FolderRenameResponse FromRawUnchecked( + IReadOnlyDictionary rawData + ) => FolderRenameResponse.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Folders/Job/JobGetParams.cs b/src/Imagekit/Models/Folders/Job/JobGetParams.cs new file mode 100644 index 00000000..35e5f68a --- /dev/null +++ b/src/Imagekit/Models/Folders/Job/JobGetParams.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Models.Folders.Job; + +/// +/// This API returns the status of a bulk job like copy and move folder operations. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class JobGetParams : ParamsBase +{ + public string? JobID { get; init; } + + public JobGetParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public JobGetParams(JobGetParams jobGetParams) + : base(jobGetParams) + { + this.JobID = jobGetParams.JobID; + } +#pragma warning restore CS8618 + + public JobGetParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + JobGetParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + string jobID + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this.JobID = jobID; + } +#pragma warning restore CS8618 + + /// + public static JobGetParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + string jobID + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + jobID + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["JobID"] = JsonSerializer.SerializeToElement(this.JobID), + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(JobGetParams? other) + { + if (other == null) + { + return false; + } + return (this.JobID?.Equals(other.JobID) ?? other.JobID == null) + && this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + string.Format("/v1/bulkJobs/{0}", this.JobID) + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/Folders/Job/JobGetResponse.cs b/src/Imagekit/Models/Folders/Job/JobGetResponse.cs new file mode 100644 index 00000000..496692cc --- /dev/null +++ b/src/Imagekit/Models/Folders/Job/JobGetResponse.cs @@ -0,0 +1,237 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models.Folders.Job; + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class JobGetResponse : JsonModel +{ + /// + /// Unique identifier of the bulk job. + /// + public string? JobID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("jobId"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("jobId", value); + } + } + + /// + /// Unique identifier of the purge request. This will be present only if `purgeCache` + /// is set to `true` in the rename folder API request. + /// + public string? PurgeRequestID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("purgeRequestId"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("purgeRequestId", value); + } + } + + /// + /// Status of the bulk job. + /// + public ApiEnum? Status + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("status"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("status", value); + } + } + + /// + /// Type of the bulk job. + /// + public ApiEnum? Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum + >("type"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("type", value); + } + } + + /// + public override void Validate() + { + _ = this.JobID; + _ = this.PurgeRequestID; + this.Status?.Validate(); + this.Type?.Validate(); + } + + public JobGetResponse() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public JobGetResponse(JobGetResponse jobGetResponse) + : base(jobGetResponse) { } +#pragma warning restore CS8618 + + public JobGetResponse(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + JobGetResponse(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static JobGetResponse FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class JobGetResponseFromRaw : IFromRawJson +{ + /// + public JobGetResponse FromRawUnchecked(IReadOnlyDictionary rawData) => + JobGetResponse.FromRawUnchecked(rawData); +} + +/// +/// Status of the bulk job. +/// +[JsonConverter(typeof(StatusConverter))] +public enum Status +{ + Pending, + Completed, +} + +sealed class StatusConverter : JsonConverter +{ + public override Status Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "Pending" => Status.Pending, + "Completed" => Status.Completed, + _ => (Status)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, Status value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + Status.Pending => "Pending", + Status.Completed => "Completed", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Type of the bulk job. +/// +[JsonConverter(typeof(TypeConverter))] +public enum Type +{ + CopyFolder, + MoveFolder, + RenameFolder, +} + +sealed class TypeConverter : JsonConverter +{ + public override global::Imagekit.Models.Folders.Job.Type Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "COPY_FOLDER" => global::Imagekit.Models.Folders.Job.Type.CopyFolder, + "MOVE_FOLDER" => global::Imagekit.Models.Folders.Job.Type.MoveFolder, + "RENAME_FOLDER" => global::Imagekit.Models.Folders.Job.Type.RenameFolder, + _ => (global::Imagekit.Models.Folders.Job.Type)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + global::Imagekit.Models.Folders.Job.Type value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + global::Imagekit.Models.Folders.Job.Type.CopyFolder => "COPY_FOLDER", + global::Imagekit.Models.Folders.Job.Type.MoveFolder => "MOVE_FOLDER", + global::Imagekit.Models.Folders.Job.Type.RenameFolder => "RENAME_FOLDER", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} diff --git a/src/Imagekit/Models/GetImageAttributesOptions.cs b/src/Imagekit/Models/GetImageAttributesOptions.cs new file mode 100644 index 00000000..3ac6dc3e --- /dev/null +++ b/src/Imagekit/Models/GetImageAttributesOptions.cs @@ -0,0 +1,540 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models; + +/// +/// Options for generating responsive image attributes including `src`, `srcSet`, +/// and `sizes` for HTML `<img>` elements. This schema extends `SrcOptions` +/// to add support for responsive image generation with breakpoints. +/// +[JsonConverter( + typeof(JsonModelConverter) +)] +public sealed record class GetImageAttributesOptions : JsonModel +{ + /// + /// Accepts a relative or absolute path of the resource. If a relative path is + /// provided, it is appended to the `urlEndpoint`. If an absolute path is provided, + /// `urlEndpoint` is ignored. + /// + public required string Src + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("src"); + } + init { this._rawData.Set("src", value); } + } + + /// + /// Get your urlEndpoint from the [ImageKit dashboard](https://imagekit.io/dashboard/url-endpoints). + /// + public required string UrlEndpoint + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("urlEndpoint"); + } + init { this._rawData.Set("urlEndpoint", value); } + } + + /// + /// When you want the signed URL to expire, specified in seconds. If `expiresIn` + /// is anything above 0, the URL will always be signed even if `signed` is set + /// to false. If not specified and `signed` is `true`, the signed URL will not + /// expire (valid indefinitely). + /// + /// Example: Setting `expiresIn: 3600` will make the URL expire 1 hour from + /// generation time. After the expiry time, the signed URL will no longer be valid + /// and ImageKit will return a 401 Unauthorized status code. + /// + /// [Learn more](https://imagekit.io/docs/media-delivery-basic-security#how-to-generate-signed-urls). + /// + public double? ExpiresIn + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("expiresIn"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("expiresIn", value); + } + } + + /// + /// These are additional query parameters that you want to add to the final URL. + /// They can be any query parameters and not necessarily related to ImageKit. + /// This is especially useful if you want to add a versioning parameter to your + /// URLs. + /// + public IReadOnlyDictionary? QueryParameters + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "queryParameters" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "queryParameters", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Whether to sign the URL or not. Set this to `true` if you want to generate + /// a signed URL. If `signed` is `true` and `expiresIn` is not specified, the + /// signed URL will not expire (valid indefinitely). Note: If `expiresIn` is + /// set to any value above 0, the URL will always be signed regardless of this + /// setting. [Learn more](https://imagekit.io/docs/media-delivery-basic-security#how-to-generate-signed-urls). + /// + public bool? Signed + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("signed"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("signed", value); + } + } + + /// + /// An array of objects specifying the transformations to be applied in the URL. + /// If more than one transformation is specified, they are applied in the order + /// they are specified as chained transformations. See [Chained transformations](https://imagekit.io/docs/transformations#chained-transformations). + /// + public IReadOnlyList? Transformation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>( + "transformation" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "transformation", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// By default, the transformation string is added as a query parameter in the + /// URL, e.g., `?tr=w-100,h-100`. If you want to add the transformation string + /// in the path of the URL, set this to `path`. Learn more in the [Transformations + /// guide](https://imagekit.io/docs/transformations). + /// + public ApiEnum? TransformationPosition + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "transformationPosition" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("transformationPosition", value); + } + } + + /// + /// Custom list of **device-width breakpoints** in pixels. These define common + /// screen widths for responsive image generation. + /// + /// Defaults to `[640, 750, 828, 1080, 1200, 1920, 2048, 3840]`. Sorted + /// automatically. + /// + public IReadOnlyList? DeviceBreakpoints + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("deviceBreakpoints"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "deviceBreakpoints", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Custom list of **image-specific breakpoints** in pixels. Useful for generating + /// small variants (e.g., placeholders or thumbnails). + /// + /// Merged with `deviceBreakpoints` before calculating `srcSet`. Defaults + /// to `[16, 32, 48, 64, 96, 128, 256, 384]`. Sorted automatically. + /// + public IReadOnlyList? ImageBreakpoints + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("imageBreakpoints"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "imageBreakpoints", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// The value for the HTML `sizes` attribute (e.g., `"100vw"` or `"(min-width:768px) + /// 50vw, 100vw"`). + /// + /// - If it includes one or more `vw` units, breakpoints smaller than the + /// corresponding percentage of the smallest device width are excluded. - If + /// it contains no `vw` units, the full breakpoint list is used. + /// + /// Enables a width-based strategy and generates `w` descriptors in `srcSet`. + /// + public string? Sizes + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("sizes"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("sizes", value); + } + } + + /// + /// The intended display width of the image in pixels, used **only when the `sizes` + /// attribute is not provided**. + /// + /// Triggers a DPR-based strategy (1x and 2x variants) and generates `x` + /// descriptors in `srcSet`. + /// + /// Ignored if `sizes` is present. + /// + public double? Width + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("width"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("width", value); + } + } + + public static implicit operator SrcOptions( + GetImageAttributesOptions getImageAttributesOptions + ) => + new() + { + Src = getImageAttributesOptions.Src, + UrlEndpoint = getImageAttributesOptions.UrlEndpoint, + ExpiresIn = getImageAttributesOptions.ExpiresIn, + QueryParameters = getImageAttributesOptions.QueryParameters, + Signed = getImageAttributesOptions.Signed, + Transformation = getImageAttributesOptions.Transformation, + TransformationPosition = getImageAttributesOptions.TransformationPosition, + }; + + /// + public override void Validate() + { + _ = this.Src; + _ = this.UrlEndpoint; + _ = this.ExpiresIn; + _ = this.QueryParameters; + _ = this.Signed; + foreach (var item in this.Transformation ?? []) + { + item.Validate(); + } + this.TransformationPosition?.Validate(); + _ = this.DeviceBreakpoints; + _ = this.ImageBreakpoints; + _ = this.Sizes; + _ = this.Width; + } + + public GetImageAttributesOptions() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public GetImageAttributesOptions(GetImageAttributesOptions getImageAttributesOptions) + : base(getImageAttributesOptions) { } +#pragma warning restore CS8618 + + public GetImageAttributesOptions(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + GetImageAttributesOptions(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static GetImageAttributesOptions FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class GetImageAttributesOptionsFromRaw : IFromRawJson +{ + /// + public GetImageAttributesOptions FromRawUnchecked( + IReadOnlyDictionary rawData + ) => GetImageAttributesOptions.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter< + GetImageAttributesOptionsGetImageAttributesOptions, + GetImageAttributesOptionsGetImageAttributesOptionsFromRaw + >) +)] +public sealed record class GetImageAttributesOptionsGetImageAttributesOptions : JsonModel +{ + /// + /// Custom list of **device-width breakpoints** in pixels. These define common + /// screen widths for responsive image generation. + /// + /// Defaults to `[640, 750, 828, 1080, 1200, 1920, 2048, 3840]`. Sorted + /// automatically. + /// + public IReadOnlyList? DeviceBreakpoints + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("deviceBreakpoints"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "deviceBreakpoints", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Custom list of **image-specific breakpoints** in pixels. Useful for generating + /// small variants (e.g., placeholders or thumbnails). + /// + /// Merged with `deviceBreakpoints` before calculating `srcSet`. Defaults + /// to `[16, 32, 48, 64, 96, 128, 256, 384]`. Sorted automatically. + /// + public IReadOnlyList? ImageBreakpoints + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("imageBreakpoints"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "imageBreakpoints", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// The value for the HTML `sizes` attribute (e.g., `"100vw"` or `"(min-width:768px) + /// 50vw, 100vw"`). + /// + /// - If it includes one or more `vw` units, breakpoints smaller than the + /// corresponding percentage of the smallest device width are excluded. - If + /// it contains no `vw` units, the full breakpoint list is used. + /// + /// Enables a width-based strategy and generates `w` descriptors in `srcSet`. + /// + public string? Sizes + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("sizes"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("sizes", value); + } + } + + /// + /// The intended display width of the image in pixels, used **only when the `sizes` + /// attribute is not provided**. + /// + /// Triggers a DPR-based strategy (1x and 2x variants) and generates `x` + /// descriptors in `srcSet`. + /// + /// Ignored if `sizes` is present. + /// + public double? Width + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("width"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("width", value); + } + } + + /// + public override void Validate() + { + _ = this.DeviceBreakpoints; + _ = this.ImageBreakpoints; + _ = this.Sizes; + _ = this.Width; + } + + public GetImageAttributesOptionsGetImageAttributesOptions() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public GetImageAttributesOptionsGetImageAttributesOptions( + GetImageAttributesOptionsGetImageAttributesOptions getImageAttributesOptionsGetImageAttributesOptions + ) + : base(getImageAttributesOptionsGetImageAttributesOptions) { } +#pragma warning restore CS8618 + + public GetImageAttributesOptionsGetImageAttributesOptions( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + GetImageAttributesOptionsGetImageAttributesOptions( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static GetImageAttributesOptionsGetImageAttributesOptions FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class GetImageAttributesOptionsGetImageAttributesOptionsFromRaw + : IFromRawJson +{ + /// + public GetImageAttributesOptionsGetImageAttributesOptions FromRawUnchecked( + IReadOnlyDictionary rawData + ) => GetImageAttributesOptionsGetImageAttributesOptions.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/ImageOverlay.cs b/src/Imagekit/Models/ImageOverlay.cs new file mode 100644 index 00000000..5cba3460 --- /dev/null +++ b/src/Imagekit/Models/ImageOverlay.cs @@ -0,0 +1,439 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models; + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class ImageOverlay : JsonModel +{ + /// + /// Controls how the layer blends with the base image or underlying content. + /// Maps to `lm` in the URL. By default, layers completely cover the base image + /// beneath them. Layer modes change this behavior: - `multiply`: Multiplies + /// the pixel values of the layer with the base image. The result is always darker + /// than the original images. This is ideal for applying shadows or color tints. + /// - `displace`: Uses the layer as a displacement map to distort pixels in the + /// base image. The red channel controls horizontal displacement, and the green + /// channel controls vertical displacement. Requires `x` or `y` parameter to control + /// displacement magnitude. - `cutout`: Acts as an inverse mask where opaque areas + /// of the layer turn the base image transparent, while transparent areas leave + /// the base image unchanged. This mode functions like a hole-punch, effectively + /// cutting the shape of the layer out of the underlying image. - `cutter`: Acts + /// as a shape mask where only the parts of the base image that fall inside the + /// opaque area of the layer are preserved. This mode functions like a cookie-cutter, + /// trimming the base image to match the specific dimensions and shape of the + /// layer. See [Layer modes](https://imagekit.io/docs/add-overlays-on-images#layer-modes). + /// + public ApiEnum? LayerMode + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("layerMode"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("layerMode", value); + } + } + + public OverlayPosition? Position + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("position"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("position", value); + } + } + + public OverlayTiming? Timing + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("timing"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("timing", value); + } + } + + /// + /// Specifies the relative path to the image used as an overlay. + /// + public required string Input + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("input"); + } + init { this._rawData.Set("input", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// The input path can be included in the layer as either `i-{input}` or `ie-{base64_encoded_input}`. + /// By default, the SDK determines the appropriate format automatically. To + /// always use base64 encoding (`ie-{base64}`), set this parameter to `base64`. + /// To always use plain text (`i-{input}`), set it to `plain`. + /// + /// Regardless of the encoding method: - Leading and trailing slashes are + /// removed. - Remaining slashes within the path are replaced with `@@` when using + /// plain text. + /// + public ApiEnum? Encoding + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("encoding"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("encoding", value); + } + } + + /// + /// Array of transformations to be applied to the overlay image. Supported transformations + /// depends on the base/parent asset. See overlays on [Images](https://imagekit.io/docs/add-overlays-on-images#list-of-supported-image-transformations-in-image-layers) + /// and [Videos](https://imagekit.io/docs/add-overlays-on-videos#list-of-transformations-supported-on-image-overlay). + /// + public IReadOnlyList? Transformation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>( + "transformation" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "transformation", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + public static implicit operator BaseOverlay(ImageOverlay imageOverlay) => + new() + { + LayerMode = imageOverlay.LayerMode, + Position = imageOverlay.Position, + Timing = imageOverlay.Timing, + }; + + /// + public override void Validate() + { + this.LayerMode?.Validate(); + this.Position?.Validate(); + this.Timing?.Validate(); + _ = this.Input; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("image"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + this.Encoding?.Validate(); + foreach (var item in this.Transformation ?? []) + { + item.Validate(); + } + } + + public ImageOverlay() + { + this.Type = JsonSerializer.SerializeToElement("image"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ImageOverlay(ImageOverlay imageOverlay) + : base(imageOverlay) { } +#pragma warning restore CS8618 + + public ImageOverlay(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("image"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ImageOverlay(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ImageOverlay FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public ImageOverlay(string input) + : this() + { + this.Input = input; + } +} + +class ImageOverlayFromRaw : IFromRawJson +{ + /// + public ImageOverlay FromRawUnchecked(IReadOnlyDictionary rawData) => + ImageOverlay.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter) +)] +public sealed record class ImageOverlayImageOverlay : JsonModel +{ + /// + /// Specifies the relative path to the image used as an overlay. + /// + public required string Input + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("input"); + } + init { this._rawData.Set("input", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// The input path can be included in the layer as either `i-{input}` or `ie-{base64_encoded_input}`. + /// By default, the SDK determines the appropriate format automatically. To + /// always use base64 encoding (`ie-{base64}`), set this parameter to `base64`. + /// To always use plain text (`i-{input}`), set it to `plain`. + /// + /// Regardless of the encoding method: - Leading and trailing slashes are + /// removed. - Remaining slashes within the path are replaced with `@@` when using + /// plain text. + /// + public ApiEnum? Encoding + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("encoding"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("encoding", value); + } + } + + /// + /// Array of transformations to be applied to the overlay image. Supported transformations + /// depends on the base/parent asset. See overlays on [Images](https://imagekit.io/docs/add-overlays-on-images#list-of-supported-image-transformations-in-image-layers) + /// and [Videos](https://imagekit.io/docs/add-overlays-on-videos#list-of-transformations-supported-on-image-overlay). + /// + public IReadOnlyList? Transformation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>( + "transformation" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "transformation", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.Input; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("image"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + this.Encoding?.Validate(); + foreach (var item in this.Transformation ?? []) + { + item.Validate(); + } + } + + public ImageOverlayImageOverlay() + { + this.Type = JsonSerializer.SerializeToElement("image"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ImageOverlayImageOverlay(ImageOverlayImageOverlay imageOverlayImageOverlay) + : base(imageOverlayImageOverlay) { } +#pragma warning restore CS8618 + + public ImageOverlayImageOverlay(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("image"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ImageOverlayImageOverlay(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ImageOverlayImageOverlay FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public ImageOverlayImageOverlay(string input) + : this() + { + this.Input = input; + } +} + +class ImageOverlayImageOverlayFromRaw : IFromRawJson +{ + /// + public ImageOverlayImageOverlay FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ImageOverlayImageOverlay.FromRawUnchecked(rawData); +} + +/// +/// The input path can be included in the layer as either `i-{input}` or `ie-{base64_encoded_input}`. +/// By default, the SDK determines the appropriate format automatically. To always +/// use base64 encoding (`ie-{base64}`), set this parameter to `base64`. To always +/// use plain text (`i-{input}`), set it to `plain`. +/// +/// Regardless of the encoding method: - Leading and trailing slashes are removed. +/// - Remaining slashes within the path are replaced with `@@` when using plain text. +/// +[JsonConverter(typeof(EncodingConverter))] +public enum Encoding +{ + Auto, + Plain, + Base64, +} + +sealed class EncodingConverter : JsonConverter +{ + public override Encoding Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "auto" => Encoding.Auto, + "plain" => Encoding.Plain, + "base64" => Encoding.Base64, + _ => (Encoding)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, Encoding value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + Encoding.Auto => "auto", + Encoding.Plain => "plain", + Encoding.Base64 => "base64", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} diff --git a/src/Imagekit/Models/Overlay.cs b/src/Imagekit/Models/Overlay.cs new file mode 100644 index 00000000..f4a3457d --- /dev/null +++ b/src/Imagekit/Models/Overlay.cs @@ -0,0 +1,529 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models; + +/// +/// Specifies an overlay to be applied on the parent image or video. ImageKit supports +/// overlays including images, text, videos, subtitles, and solid colors. See [Overlay +/// using layers](https://imagekit.io/docs/transformations#overlay-using-layers). +/// +[JsonConverter(typeof(OverlayConverter))] +public record class Overlay : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public ApiEnum? LayerMode + { + get + { + return Match?>( + text: (x) => x.LayerMode, + image: (x) => x.LayerMode, + video: (x) => x.LayerMode, + subtitle: (x) => x.LayerMode, + solidColor: (x) => x.LayerMode + ); + } + } + + public OverlayPosition? Position + { + get + { + return Match( + text: (x) => x.Position, + image: (x) => x.Position, + video: (x) => x.Position, + subtitle: (x) => x.Position, + solidColor: (x) => x.Position + ); + } + } + + public OverlayTiming? Timing + { + get + { + return Match( + text: (x) => x.Timing, + image: (x) => x.Timing, + video: (x) => x.Timing, + subtitle: (x) => x.Timing, + solidColor: (x) => x.Timing + ); + } + } + + public JsonElement Type + { + get + { + return Match( + text: (x) => x.Type, + image: (x) => x.Type, + video: (x) => x.Type, + subtitle: (x) => x.Type, + solidColor: (x) => x.Type + ); + } + } + + public string? Input + { + get + { + return Match( + text: (_) => null, + image: (x) => x.Input, + video: (x) => x.Input, + subtitle: (x) => x.Input, + solidColor: (_) => null + ); + } + } + + public Overlay(TextOverlay value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Overlay(ImageOverlay value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Overlay(VideoOverlay value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Overlay(SubtitleOverlay value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Overlay(SolidColorOverlay value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Overlay(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickText(out var value)) { + /// // `value` is of type `TextOverlay` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickText([NotNullWhen(true)] out TextOverlay? value) + { + value = this.Value as TextOverlay; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickImage(out var value)) { + /// // `value` is of type `ImageOverlay` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickImage([NotNullWhen(true)] out ImageOverlay? value) + { + value = this.Value as ImageOverlay; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickVideo(out var value)) { + /// // `value` is of type `VideoOverlay` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickVideo([NotNullWhen(true)] out VideoOverlay? value) + { + value = this.Value as VideoOverlay; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickSubtitle(out var value)) { + /// // `value` is of type `SubtitleOverlay` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickSubtitle([NotNullWhen(true)] out SubtitleOverlay? value) + { + value = this.Value as SubtitleOverlay; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickSolidColor(out var value)) { + /// // `value` is of type `SolidColorOverlay` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickSolidColor([NotNullWhen(true)] out SolidColorOverlay? value) + { + value = this.Value as SolidColorOverlay; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (TextOverlay value) => {...}, + /// (ImageOverlay value) => {...}, + /// (VideoOverlay value) => {...}, + /// (SubtitleOverlay value) => {...}, + /// (SolidColorOverlay value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action text, + System::Action image, + System::Action video, + System::Action subtitle, + System::Action solidColor + ) + { + switch (this.Value) + { + case TextOverlay value: + text(value); + break; + case ImageOverlay value: + image(value); + break; + case VideoOverlay value: + video(value); + break; + case SubtitleOverlay value: + subtitle(value); + break; + case SolidColorOverlay value: + solidColor(value); + break; + default: + throw new ImageKitInvalidDataException("Data did not match any variant of Overlay"); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (TextOverlay value) => {...}, + /// (ImageOverlay value) => {...}, + /// (VideoOverlay value) => {...}, + /// (SubtitleOverlay value) => {...}, + /// (SolidColorOverlay value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func text, + System::Func image, + System::Func video, + System::Func subtitle, + System::Func solidColor + ) + { + return this.Value switch + { + TextOverlay value => text(value), + ImageOverlay value => image(value), + VideoOverlay value => video(value), + SubtitleOverlay value => subtitle(value), + SolidColorOverlay value => solidColor(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of Overlay" + ), + }; + } + + public static implicit operator Overlay(TextOverlay value) => new(value); + + public static implicit operator Overlay(ImageOverlay value) => new(value); + + public static implicit operator Overlay(VideoOverlay value) => new(value); + + public static implicit operator Overlay(SubtitleOverlay value) => new(value); + + public static implicit operator Overlay(SolidColorOverlay value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of Overlay"); + } + this.Switch( + (text) => text.Validate(), + (image) => image.Validate(), + (video) => video.Validate(), + (subtitle) => subtitle.Validate(), + (solidColor) => solidColor.Validate() + ); + } + + public virtual bool Equals(Overlay? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + TextOverlay _ => 0, + ImageOverlay _ => 1, + VideoOverlay _ => 2, + SubtitleOverlay _ => 3, + SolidColorOverlay _ => 4, + _ => -1, + }; + } +} + +sealed class OverlayConverter : JsonConverter +{ + public override Overlay? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + string? type; + try + { + type = element.GetProperty("type").GetString(); + } + catch + { + type = null; + } + + switch (type) + { + case "text": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "image": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "video": + { + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "subtitle": + { + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + case "solidColor": + { + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (JsonException) + { + // ignore + } + + return new(element); + } + default: + { + return new Overlay(element); + } + } + } + + public override void Write(Utf8JsonWriter writer, Overlay value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} diff --git a/src/Imagekit/Models/OverlayPosition.cs b/src/Imagekit/Models/OverlayPosition.cs new file mode 100644 index 00000000..011e9281 --- /dev/null +++ b/src/Imagekit/Models/OverlayPosition.cs @@ -0,0 +1,1293 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models; + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class OverlayPosition : JsonModel +{ + /// + /// Sets the anchor point on the base asset from which the overlay offset is + /// calculated. The default value is `top_left`. Maps to `lap` in the URL. Can + /// only be used with one or more of `x`, `y`, `xCenter`, or `yCenter`. + /// + public ApiEnum? AnchorPoint + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("anchorPoint"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("anchorPoint", value); + } + } + + /// + /// Specifies the position of the overlay relative to the parent image or video. + /// If one or more of `x`, `y`, `xCenter`, or `yCenter` parameters are specified, + /// this parameter is ignored. Maps to `lfo` in the URL. + /// + public ApiEnum? Focus + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("focus"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("focus", value); + } + } + + /// + /// Specifies the x-coordinate of the top-left corner of the base asset where + /// the overlay's top-left corner will be positioned. It also accepts arithmetic + /// expressions such as `bw_mul_0.4` or `bw_sub_cw`. Maps to `lx` in the URL. + /// Learn about [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). + /// + public X? X + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("x"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("x", value); + } + } + + /// + /// Specifies the x-coordinate on the base asset where the overlay's center will + /// be positioned. It also accepts arithmetic expressions such as `bw_mul_0.4` + /// or `bw_sub_cw`. Maps to `lxc` in the URL. Cannot be used together with `x`, + /// but can be used with `y`. Learn about [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). + /// + public XCenter? XCenter + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("xCenter"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("xCenter", value); + } + } + + /// + /// Specifies the y-coordinate of the top-left corner of the base asset where + /// the overlay's top-left corner will be positioned. It also accepts arithmetic + /// expressions such as `bh_mul_0.4` or `bh_sub_ch`. Maps to `ly` in the URL. + /// Learn about [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). + /// + public Y? Y + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("y"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("y", value); + } + } + + /// + /// Specifies the y-coordinate on the base asset where the overlay's center will + /// be positioned. It also accepts arithmetic expressions such as `bh_mul_0.4` + /// or `bh_sub_ch`. Maps to `lyc` in the URL. Cannot be used together with `y`, + /// but can be used with `x`. Learn about [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). + /// + public YCenter? YCenter + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("yCenter"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("yCenter", value); + } + } + + /// + public override void Validate() + { + this.AnchorPoint?.Validate(); + this.Focus?.Validate(); + this.X?.Validate(); + this.XCenter?.Validate(); + this.Y?.Validate(); + this.YCenter?.Validate(); + } + + public OverlayPosition() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public OverlayPosition(OverlayPosition overlayPosition) + : base(overlayPosition) { } +#pragma warning restore CS8618 + + public OverlayPosition(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + OverlayPosition(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static OverlayPosition FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class OverlayPositionFromRaw : IFromRawJson +{ + /// + public OverlayPosition FromRawUnchecked(IReadOnlyDictionary rawData) => + OverlayPosition.FromRawUnchecked(rawData); +} + +/// +/// Sets the anchor point on the base asset from which the overlay offset is calculated. +/// The default value is `top_left`. Maps to `lap` in the URL. Can only be used with +/// one or more of `x`, `y`, `xCenter`, or `yCenter`. +/// +[JsonConverter(typeof(AnchorPointConverter))] +public enum AnchorPoint +{ + Top, + Left, + Right, + Bottom, + TopLeft, + TopRight, + BottomLeft, + BottomRight, + Center, +} + +sealed class AnchorPointConverter : JsonConverter +{ + public override AnchorPoint Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "top" => AnchorPoint.Top, + "left" => AnchorPoint.Left, + "right" => AnchorPoint.Right, + "bottom" => AnchorPoint.Bottom, + "top_left" => AnchorPoint.TopLeft, + "top_right" => AnchorPoint.TopRight, + "bottom_left" => AnchorPoint.BottomLeft, + "bottom_right" => AnchorPoint.BottomRight, + "center" => AnchorPoint.Center, + _ => (AnchorPoint)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + AnchorPoint value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + AnchorPoint.Top => "top", + AnchorPoint.Left => "left", + AnchorPoint.Right => "right", + AnchorPoint.Bottom => "bottom", + AnchorPoint.TopLeft => "top_left", + AnchorPoint.TopRight => "top_right", + AnchorPoint.BottomLeft => "bottom_left", + AnchorPoint.BottomRight => "bottom_right", + AnchorPoint.Center => "center", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Specifies the position of the overlay relative to the parent image or video. If +/// one or more of `x`, `y`, `xCenter`, or `yCenter` parameters are specified, this +/// parameter is ignored. Maps to `lfo` in the URL. +/// +[JsonConverter(typeof(FocusConverter))] +public enum Focus +{ + Center, + Top, + Left, + Bottom, + Right, + TopLeft, + TopRight, + BottomLeft, + BottomRight, +} + +sealed class FocusConverter : JsonConverter +{ + public override Focus Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "center" => Focus.Center, + "top" => Focus.Top, + "left" => Focus.Left, + "bottom" => Focus.Bottom, + "right" => Focus.Right, + "top_left" => Focus.TopLeft, + "top_right" => Focus.TopRight, + "bottom_left" => Focus.BottomLeft, + "bottom_right" => Focus.BottomRight, + _ => (Focus)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, Focus value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + Focus.Center => "center", + Focus.Top => "top", + Focus.Left => "left", + Focus.Bottom => "bottom", + Focus.Right => "right", + Focus.TopLeft => "top_left", + Focus.TopRight => "top_right", + Focus.BottomLeft => "bottom_left", + Focus.BottomRight => "bottom_right", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Specifies the x-coordinate of the top-left corner of the base asset where the +/// overlay's top-left corner will be positioned. It also accepts arithmetic expressions +/// such as `bw_mul_0.4` or `bw_sub_cw`. Maps to `lx` in the URL. Learn about [Arithmetic +/// expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). +/// +[JsonConverter(typeof(XConverter))] +public record class X : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public X(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public X(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public X(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException("Data did not match any variant of X"); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException("Data did not match any variant of X"), + }; + } + + public static implicit operator X(double value) => new(value); + + public static implicit operator X(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of X"); + } + } + + public virtual bool Equals(X? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class XConverter : JsonConverter +{ + public override X? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, X value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Specifies the x-coordinate on the base asset where the overlay's center will +/// be positioned. It also accepts arithmetic expressions such as `bw_mul_0.4` or +/// `bw_sub_cw`. Maps to `lxc` in the URL. Cannot be used together with `x`, but can +/// be used with `y`. Learn about [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). +/// +[JsonConverter(typeof(XCenterConverter))] +public record class XCenter : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public XCenter(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public XCenter(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public XCenter(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException("Data did not match any variant of XCenter"); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of XCenter" + ), + }; + } + + public static implicit operator XCenter(double value) => new(value); + + public static implicit operator XCenter(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of XCenter"); + } + } + + public virtual bool Equals(XCenter? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class XCenterConverter : JsonConverter +{ + public override XCenter? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, XCenter value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Specifies the y-coordinate of the top-left corner of the base asset where the +/// overlay's top-left corner will be positioned. It also accepts arithmetic expressions +/// such as `bh_mul_0.4` or `bh_sub_ch`. Maps to `ly` in the URL. Learn about [Arithmetic +/// expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). +/// +[JsonConverter(typeof(YConverter))] +public record class Y : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public Y(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Y(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Y(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException("Data did not match any variant of Y"); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException("Data did not match any variant of Y"), + }; + } + + public static implicit operator Y(double value) => new(value); + + public static implicit operator Y(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of Y"); + } + } + + public virtual bool Equals(Y? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class YConverter : JsonConverter +{ + public override Y? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, Y value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Specifies the y-coordinate on the base asset where the overlay's center will +/// be positioned. It also accepts arithmetic expressions such as `bh_mul_0.4` or +/// `bh_sub_ch`. Maps to `lyc` in the URL. Cannot be used together with `y`, but can +/// be used with `x`. Learn about [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). +/// +[JsonConverter(typeof(YCenterConverter))] +public record class YCenter : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public YCenter(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public YCenter(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public YCenter(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException("Data did not match any variant of YCenter"); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of YCenter" + ), + }; + } + + public static implicit operator YCenter(double value) => new(value); + + public static implicit operator YCenter(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of YCenter"); + } + } + + public virtual bool Equals(YCenter? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class YCenterConverter : JsonConverter +{ + public override YCenter? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, YCenter value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} diff --git a/src/Imagekit/Models/OverlayTiming.cs b/src/Imagekit/Models/OverlayTiming.cs new file mode 100644 index 00000000..64b61015 --- /dev/null +++ b/src/Imagekit/Models/OverlayTiming.cs @@ -0,0 +1,848 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models; + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class OverlayTiming : JsonModel +{ + /// + /// Specifies the duration (in seconds) during which the overlay should appear + /// on the base video. Accepts a positive number up to two decimal places (e.g., + /// `20` or `20.50`) and arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. + /// Applies only if the base asset is a video. Maps to `ldu` in the URL. + /// + public Duration? Duration + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("duration"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("duration", value); + } + } + + /// + /// Specifies the end time (in seconds) for when the overlay should disappear + /// from the base video. If both end and duration are provided, duration is ignored. + /// Accepts a positive number up to two decimal places (e.g., `20` or `20.50`) + /// and arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. Applies + /// only if the base asset is a video. Maps to `leo` in the URL. + /// + public End? End + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("end"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("end", value); + } + } + + /// + /// Specifies the start time (in seconds) for when the overlay should appear + /// on the base video. Accepts a positive number up to two decimal places (e.g., + /// `20` or `20.50`) and arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. + /// Applies only if the base asset is a video. Maps to `lso` in the URL. + /// + public Start? Start + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("start"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("start", value); + } + } + + /// + public override void Validate() + { + this.Duration?.Validate(); + this.End?.Validate(); + this.Start?.Validate(); + } + + public OverlayTiming() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public OverlayTiming(OverlayTiming overlayTiming) + : base(overlayTiming) { } +#pragma warning restore CS8618 + + public OverlayTiming(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + OverlayTiming(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static OverlayTiming FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class OverlayTimingFromRaw : IFromRawJson +{ + /// + public OverlayTiming FromRawUnchecked(IReadOnlyDictionary rawData) => + OverlayTiming.FromRawUnchecked(rawData); +} + +/// +/// Specifies the duration (in seconds) during which the overlay should appear on +/// the base video. Accepts a positive number up to two decimal places (e.g., `20` +/// or `20.50`) and arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. +/// Applies only if the base asset is a video. Maps to `ldu` in the URL. +/// +[JsonConverter(typeof(DurationConverter))] +public record class Duration : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public Duration(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Duration(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Duration(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of Duration" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of Duration" + ), + }; + } + + public static implicit operator Duration(double value) => new(value); + + public static implicit operator Duration(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of Duration"); + } + } + + public virtual bool Equals(Duration? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class DurationConverter : JsonConverter +{ + public override Duration? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, Duration value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Specifies the end time (in seconds) for when the overlay should disappear from +/// the base video. If both end and duration are provided, duration is ignored. Accepts +/// a positive number up to two decimal places (e.g., `20` or `20.50`) and arithmetic +/// expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. Applies only if the base +/// asset is a video. Maps to `leo` in the URL. +/// +[JsonConverter(typeof(EndConverter))] +public record class End : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public End(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public End(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public End(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException("Data did not match any variant of End"); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException("Data did not match any variant of End"), + }; + } + + public static implicit operator End(double value) => new(value); + + public static implicit operator End(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of End"); + } + } + + public virtual bool Equals(End? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class EndConverter : JsonConverter +{ + public override End? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, End value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Specifies the start time (in seconds) for when the overlay should appear on the +/// base video. Accepts a positive number up to two decimal places (e.g., `20` or +/// `20.50`) and arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. Applies +/// only if the base asset is a video. Maps to `lso` in the URL. +/// +[JsonConverter(typeof(StartConverter))] +public record class Start : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public Start(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Start(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Start(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException("Data did not match any variant of Start"); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException("Data did not match any variant of Start"), + }; + } + + public static implicit operator Start(double value) => new(value); + + public static implicit operator Start(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of Start"); + } + } + + public virtual bool Equals(Start? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class StartConverter : JsonConverter +{ + public override Start? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, Start value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} diff --git a/src/Imagekit/Models/ResponsiveImageAttributes.cs b/src/Imagekit/Models/ResponsiveImageAttributes.cs new file mode 100644 index 00000000..b719168a --- /dev/null +++ b/src/Imagekit/Models/ResponsiveImageAttributes.cs @@ -0,0 +1,149 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models; + +/// +/// Resulting set of attributes suitable for an HTML `<img>` element. Useful +/// for enabling responsive image loading with `srcSet` and `sizes`. +/// +[JsonConverter( + typeof(JsonModelConverter) +)] +public sealed record class ResponsiveImageAttributes : JsonModel +{ + /// + /// URL for the *largest* candidate (assigned to plain `src`). + /// + public required string Src + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("src"); + } + init { this._rawData.Set("src", value); } + } + + /// + /// `sizes` returned (or synthesised as `100vw`). The value for the HTML `sizes` + /// attribute. + /// + public string? Sizes + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("sizes"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("sizes", value); + } + } + + /// + /// Candidate set with `w` or `x` descriptors. Multiple image URLs separated + /// by commas, each with a descriptor. + /// + public string? SrcSet + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("srcSet"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("srcSet", value); + } + } + + /// + /// Width as a number (if `width` was provided in the input options). + /// + public double? Width + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("width"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("width", value); + } + } + + /// + public override void Validate() + { + _ = this.Src; + _ = this.Sizes; + _ = this.SrcSet; + _ = this.Width; + } + + public ResponsiveImageAttributes() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ResponsiveImageAttributes(ResponsiveImageAttributes responsiveImageAttributes) + : base(responsiveImageAttributes) { } +#pragma warning restore CS8618 + + public ResponsiveImageAttributes(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ResponsiveImageAttributes(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ResponsiveImageAttributes FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public ResponsiveImageAttributes(string src) + : this() + { + this.Src = src; + } +} + +class ResponsiveImageAttributesFromRaw : IFromRawJson +{ + /// + public ResponsiveImageAttributes FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ResponsiveImageAttributes.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/SavedExtensions/SavedExtensionCreateParams.cs b/src/Imagekit/Models/SavedExtensions/SavedExtensionCreateParams.cs new file mode 100644 index 00000000..cbc65193 --- /dev/null +++ b/src/Imagekit/Models/SavedExtensions/SavedExtensionCreateParams.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; +using Text = System.Text; + +namespace Imagekit.Models.SavedExtensions; + +/// +/// This API creates a new saved extension. Saved extensions allow you to save complex +/// extension configurations (like AI tasks) and reuse them by referencing the ID +/// in upload or update file APIs. +/// +/// **Saved extension limit** \ You can create a maximum of 100 saved extensions +/// per account. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class SavedExtensionCreateParams : ParamsBase +{ + readonly JsonDictionary _rawBodyData = new(); + public IReadOnlyDictionary RawBodyData + { + get { return this._rawBodyData.Freeze(); } + } + + /// + /// Configuration object for an extension (base extensions only, not saved extension references). + /// + public required ExtensionConfig Config + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("config"); + } + init { this._rawBodyData.Set("config", value); } + } + + /// + /// Description of what the saved extension does. + /// + public required string Description + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("description"); + } + init { this._rawBodyData.Set("description", value); } + } + + /// + /// Name of the saved extension. + /// + public required string Name + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("name"); + } + init { this._rawBodyData.Set("name", value); } + } + + public SavedExtensionCreateParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SavedExtensionCreateParams(SavedExtensionCreateParams savedExtensionCreateParams) + : base(savedExtensionCreateParams) + { + this._rawBodyData = new(savedExtensionCreateParams._rawBodyData); + } +#pragma warning restore CS8618 + + public SavedExtensionCreateParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SavedExtensionCreateParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + FrozenDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } +#pragma warning restore CS8618 + + /// + public static SavedExtensionCreateParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + FrozenDictionary.ToFrozenDictionary(rawBodyData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this._rawBodyData.Freeze()), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(SavedExtensionCreateParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this._rawBodyData.Equals(other._rawBodyData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder(options.BaseUrl.ToString().TrimEnd('/') + "/v1/saved-extensions") + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/SavedExtensions/SavedExtensionDeleteParams.cs b/src/Imagekit/Models/SavedExtensions/SavedExtensionDeleteParams.cs new file mode 100644 index 00000000..065755e1 --- /dev/null +++ b/src/Imagekit/Models/SavedExtensions/SavedExtensionDeleteParams.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Models.SavedExtensions; + +/// +/// This API deletes a saved extension permanently. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class SavedExtensionDeleteParams : ParamsBase +{ + public string? ID { get; init; } + + public SavedExtensionDeleteParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SavedExtensionDeleteParams(SavedExtensionDeleteParams savedExtensionDeleteParams) + : base(savedExtensionDeleteParams) + { + this.ID = savedExtensionDeleteParams.ID; + } +#pragma warning restore CS8618 + + public SavedExtensionDeleteParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SavedExtensionDeleteParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + string id + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this.ID = id; + } +#pragma warning restore CS8618 + + /// + public static SavedExtensionDeleteParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + string id + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + id + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["ID"] = JsonSerializer.SerializeToElement(this.ID), + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(SavedExtensionDeleteParams? other) + { + if (other == null) + { + return false; + } + return (this.ID?.Equals(other.ID) ?? other.ID == null) + && this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + + string.Format("/v1/saved-extensions/{0}", this.ID) + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/SavedExtensions/SavedExtensionGetParams.cs b/src/Imagekit/Models/SavedExtensions/SavedExtensionGetParams.cs new file mode 100644 index 00000000..e72d7eae --- /dev/null +++ b/src/Imagekit/Models/SavedExtensions/SavedExtensionGetParams.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Models.SavedExtensions; + +/// +/// This API returns details of a specific saved extension by ID. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class SavedExtensionGetParams : ParamsBase +{ + public string? ID { get; init; } + + public SavedExtensionGetParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SavedExtensionGetParams(SavedExtensionGetParams savedExtensionGetParams) + : base(savedExtensionGetParams) + { + this.ID = savedExtensionGetParams.ID; + } +#pragma warning restore CS8618 + + public SavedExtensionGetParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SavedExtensionGetParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + string id + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this.ID = id; + } +#pragma warning restore CS8618 + + /// + public static SavedExtensionGetParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + string id + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + id + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["ID"] = JsonSerializer.SerializeToElement(this.ID), + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(SavedExtensionGetParams? other) + { + if (other == null) + { + return false; + } + return (this.ID?.Equals(other.ID) ?? other.ID == null) + && this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + + string.Format("/v1/saved-extensions/{0}", this.ID) + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/SavedExtensions/SavedExtensionListParams.cs b/src/Imagekit/Models/SavedExtensions/SavedExtensionListParams.cs new file mode 100644 index 00000000..3200e681 --- /dev/null +++ b/src/Imagekit/Models/SavedExtensions/SavedExtensionListParams.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; + +namespace Imagekit.Models.SavedExtensions; + +/// +/// This API returns an array of all saved extensions for your account. Saved extensions +/// allow you to save complex extension configurations and reuse them by referencing +/// them by ID in upload or update file APIs. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class SavedExtensionListParams : ParamsBase +{ + public SavedExtensionListParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SavedExtensionListParams(SavedExtensionListParams savedExtensionListParams) + : base(savedExtensionListParams) { } +#pragma warning restore CS8618 + + public SavedExtensionListParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SavedExtensionListParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + } +#pragma warning restore CS8618 + + /// + public static SavedExtensionListParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData) + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(SavedExtensionListParams? other) + { + if (other == null) + { + return false; + } + return this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder(options.BaseUrl.ToString().TrimEnd('/') + "/v1/saved-extensions") + { + Query = this.QueryString(options), + }.Uri; + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/SavedExtensions/SavedExtensionUpdateParams.cs b/src/Imagekit/Models/SavedExtensions/SavedExtensionUpdateParams.cs new file mode 100644 index 00000000..b08ea714 --- /dev/null +++ b/src/Imagekit/Models/SavedExtensions/SavedExtensionUpdateParams.cs @@ -0,0 +1,212 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.Json; +using Imagekit.Core; +using Text = System.Text; + +namespace Imagekit.Models.SavedExtensions; + +/// +/// This API updates an existing saved extension. You can update the name, description, +/// or config. +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with +/// breaking changes in non-major versions. We may add new methods in the future that +/// cause existing derived classes to break. +/// +public record class SavedExtensionUpdateParams : ParamsBase +{ + readonly JsonDictionary _rawBodyData = new(); + public IReadOnlyDictionary RawBodyData + { + get { return this._rawBodyData.Freeze(); } + } + + public string? ID { get; init; } + + /// + /// Configuration object for an extension (base extensions only, not saved extension references). + /// + public ExtensionConfig? Config + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("config"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("config", value); + } + } + + /// + /// Updated description of the saved extension. + /// + public string? Description + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("description"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("description", value); + } + } + + /// + /// Updated name of the saved extension. + /// + public string? Name + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("name"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("name", value); + } + } + + public SavedExtensionUpdateParams() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SavedExtensionUpdateParams(SavedExtensionUpdateParams savedExtensionUpdateParams) + : base(savedExtensionUpdateParams) + { + this.ID = savedExtensionUpdateParams.ID; + + this._rawBodyData = new(savedExtensionUpdateParams._rawBodyData); + } +#pragma warning restore CS8618 + + public SavedExtensionUpdateParams( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SavedExtensionUpdateParams( + FrozenDictionary rawHeaderData, + FrozenDictionary rawQueryData, + FrozenDictionary rawBodyData, + string id + ) + { + this._rawHeaderData = new(rawHeaderData); + this._rawQueryData = new(rawQueryData); + this._rawBodyData = new(rawBodyData); + this.ID = id; + } +#pragma warning restore CS8618 + + /// + public static SavedExtensionUpdateParams FromRawUnchecked( + IReadOnlyDictionary rawHeaderData, + IReadOnlyDictionary rawQueryData, + IReadOnlyDictionary rawBodyData, + string id + ) + { + return new( + FrozenDictionary.ToFrozenDictionary(rawHeaderData), + FrozenDictionary.ToFrozenDictionary(rawQueryData), + FrozenDictionary.ToFrozenDictionary(rawBodyData), + id + ); + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue( + new Dictionary() + { + ["ID"] = JsonSerializer.SerializeToElement(this.ID), + ["HeaderData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawHeaderData.Freeze()) + ), + ["QueryData"] = FriendlyJsonPrinter.PrintValue( + JsonSerializer.SerializeToElement(this._rawQueryData.Freeze()) + ), + ["BodyData"] = FriendlyJsonPrinter.PrintValue(this._rawBodyData.Freeze()), + } + ), + ModelBase.ToStringSerializerOptions + ); + + public virtual bool Equals(SavedExtensionUpdateParams? other) + { + if (other == null) + { + return false; + } + return (this.ID?.Equals(other.ID) ?? other.ID == null) + && this._rawHeaderData.Equals(other._rawHeaderData) + && this._rawQueryData.Equals(other._rawQueryData) + && this._rawBodyData.Equals(other._rawBodyData); + } + + public override Uri Url(ClientOptions options) + { + return new UriBuilder( + options.BaseUrl.ToString().TrimEnd('/') + + string.Format("/v1/saved-extensions/{0}", this.ID) + ) + { + Query = this.QueryString(options), + }.Uri; + } + + internal override HttpContent? BodyContent() + { + return new StringContent( + JsonSerializer.Serialize(this.RawBodyData, ModelBase.SerializerOptions), + Text::Encoding.UTF8, + "application/json" + ); + } + + internal override void AddHeadersToRequest(HttpRequestMessage request, ClientOptions options) + { + ParamsBase.AddDefaultHeaders(request, options); + foreach (var item in this.RawHeaderData) + { + ParamsBase.AddHeaderElementToRequest(request, item.Key, item.Value); + } + } + + public override int GetHashCode() + { + return 0; + } +} diff --git a/src/Imagekit/Models/SelectedFieldsSchemaItem.cs b/src/Imagekit/Models/SelectedFieldsSchemaItem.cs new file mode 100644 index 00000000..9ec4ad75 --- /dev/null +++ b/src/Imagekit/Models/SelectedFieldsSchemaItem.cs @@ -0,0 +1,1798 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models; + +[JsonConverter( + typeof(JsonModelConverter) +)] +public sealed record class SelectedFieldsSchemaItem : JsonModel +{ + /// + /// Type of the custom metadata field. + /// + public required ApiEnum Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass>( + "type" + ); + } + init { this._rawData.Set("type", value); } + } + + /// + /// The default value for this custom metadata field. The value should match + /// the `type` of custom metadata field. + /// + public DefaultValue? DefaultValue + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("defaultValue"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("defaultValue", value); + } + } + + /// + /// Specifies if the custom metadata field is required or not. + /// + public bool? IsValueRequired + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("isValueRequired"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("isValueRequired", value); + } + } + + /// + /// Maximum length of string. Only set if `type` is set to `Text` or `Textarea`. + /// + public double? MaxLength + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("maxLength"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("maxLength", value); + } + } + + /// + /// Maximum value of the field. Only set if field type is `Date` or `Number`. + /// For `Date` type field, the value will be in ISO8601 string format. For `Number` + /// type field, it will be a numeric value. + /// + public MaxValue? MaxValue + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("maxValue"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("maxValue", value); + } + } + + /// + /// Minimum length of string. Only set if `type` is set to `Text` or `Textarea`. + /// + public double? MinLength + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("minLength"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("minLength", value); + } + } + + /// + /// Minimum value of the field. Only set if field type is `Date` or `Number`. + /// For `Date` type field, the value will be in ISO8601 string format. For `Number` + /// type field, it will be a numeric value. + /// + public MinValue? MinValue + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("minValue"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("minValue", value); + } + } + + /// + /// Indicates whether the custom metadata field is read only. A read only field + /// cannot be modified after being set. This field is configurable only via the + /// **Path policy** feature. + /// + public bool? ReadOnly + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("readOnly"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("readOnly", value); + } + } + + /// + /// An array of allowed values when field type is `SingleSelect` or `MultiSelect`. + /// + public IReadOnlyList? SelectOptions + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("selectOptions"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "selectOptions", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Specifies if the selectOptions array is truncated. It is truncated when number + /// of options are > 100. + /// + public bool? SelectOptionsTruncated + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("selectOptionsTruncated"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("selectOptionsTruncated", value); + } + } + + /// + public override void Validate() + { + this.Type.Validate(); + this.DefaultValue?.Validate(); + _ = this.IsValueRequired; + _ = this.MaxLength; + this.MaxValue?.Validate(); + _ = this.MinLength; + this.MinValue?.Validate(); + _ = this.ReadOnly; + foreach (var item in this.SelectOptions ?? []) + { + item.Validate(); + } + _ = this.SelectOptionsTruncated; + } + + public SelectedFieldsSchemaItem() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SelectedFieldsSchemaItem(SelectedFieldsSchemaItem selectedFieldsSchemaItem) + : base(selectedFieldsSchemaItem) { } +#pragma warning restore CS8618 + + public SelectedFieldsSchemaItem(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SelectedFieldsSchemaItem(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SelectedFieldsSchemaItem FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public SelectedFieldsSchemaItem(ApiEnum type) + : this() + { + this.Type = type; + } +} + +class SelectedFieldsSchemaItemFromRaw : IFromRawJson +{ + /// + public SelectedFieldsSchemaItem FromRawUnchecked( + IReadOnlyDictionary rawData + ) => SelectedFieldsSchemaItem.FromRawUnchecked(rawData); +} + +/// +/// Type of the custom metadata field. +/// +[JsonConverter(typeof(TypeConverter))] +public enum Type +{ + Text, + Textarea, + Number, + Date, + Boolean, + SingleSelect, + MultiSelect, +} + +sealed class TypeConverter : JsonConverter +{ + public override global::Imagekit.Models.Type Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "Text" => global::Imagekit.Models.Type.Text, + "Textarea" => global::Imagekit.Models.Type.Textarea, + "Number" => global::Imagekit.Models.Type.Number, + "Date" => global::Imagekit.Models.Type.Date, + "Boolean" => global::Imagekit.Models.Type.Boolean, + "SingleSelect" => global::Imagekit.Models.Type.SingleSelect, + "MultiSelect" => global::Imagekit.Models.Type.MultiSelect, + _ => (global::Imagekit.Models.Type)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + global::Imagekit.Models.Type value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + global::Imagekit.Models.Type.Text => "Text", + global::Imagekit.Models.Type.Textarea => "Textarea", + global::Imagekit.Models.Type.Number => "Number", + global::Imagekit.Models.Type.Date => "Date", + global::Imagekit.Models.Type.Boolean => "Boolean", + global::Imagekit.Models.Type.SingleSelect => "SingleSelect", + global::Imagekit.Models.Type.MultiSelect => "MultiSelect", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// The default value for this custom metadata field. The value should match the `type` +/// of custom metadata field. +/// +[JsonConverter(typeof(DefaultValueConverter))] +public record class DefaultValue : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public DefaultValue(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public DefaultValue(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public DefaultValue(bool value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public DefaultValue(IReadOnlyList value, JsonElement? element = null) + { + this.Value = ImmutableArray.ToImmutableArray(value); + this._element = element; + } + + public DefaultValue(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type where T is a DefaultValueArrayItem. + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickMixed(out var value)) { + /// // `value` is of type `IReadOnlyList<DefaultValueArrayItem>` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickMixed([NotNullWhen(true)] out IReadOnlyList? value) + { + value = this.Value as IReadOnlyList; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...}, + /// (IReadOnlyList<DefaultValueArrayItem> value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool, + System::Action> mixed + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + case IReadOnlyList value: + mixed(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of DefaultValue" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...}, + /// (IReadOnlyList<DefaultValueArrayItem> value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool, + System::Func, T> mixed + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + IReadOnlyList value => mixed(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of DefaultValue" + ), + }; + } + + public static implicit operator DefaultValue(string value) => new(value); + + public static implicit operator DefaultValue(double value) => new(value); + + public static implicit operator DefaultValue(bool value) => new(value); + + public static implicit operator DefaultValue(List value) => + new((IReadOnlyList)value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of DefaultValue" + ); + } + this.Switch( + (_) => { }, + (_) => { }, + (_) => { }, + (mixed) => + { + foreach (var item in mixed) + { + item.Validate(); + } + } + ); + } + + public virtual bool Equals(DefaultValue? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + IReadOnlyList _ => 3, + _ => -1, + }; + } +} + +sealed class DefaultValueConverter : JsonConverter +{ + public override DefaultValue? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize>( + element, + options + ); + if (deserialized != null) + { + foreach (var item in deserialized) + { + item.Validate(); + } + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + DefaultValue value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(DefaultValueArrayItemConverter))] +public record class DefaultValueArrayItem : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public DefaultValueArrayItem(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public DefaultValueArrayItem(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public DefaultValueArrayItem(bool value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public DefaultValueArrayItem(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of DefaultValueArrayItem" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of DefaultValueArrayItem" + ), + }; + } + + public static implicit operator DefaultValueArrayItem(string value) => new(value); + + public static implicit operator DefaultValueArrayItem(double value) => new(value); + + public static implicit operator DefaultValueArrayItem(bool value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of DefaultValueArrayItem" + ); + } + } + + public virtual bool Equals(DefaultValueArrayItem? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + _ => -1, + }; + } +} + +sealed class DefaultValueArrayItemConverter : JsonConverter +{ + public override DefaultValueArrayItem? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + DefaultValueArrayItem value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Maximum value of the field. Only set if field type is `Date` or `Number`. For +/// `Date` type field, the value will be in ISO8601 string format. For `Number` type +/// field, it will be a numeric value. +/// +[JsonConverter(typeof(MaxValueConverter))] +public record class MaxValue : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public MaxValue(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public MaxValue(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public MaxValue(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @string, System::Action @double) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of MaxValue" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @string, System::Func @double) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of MaxValue" + ), + }; + } + + public static implicit operator MaxValue(string value) => new(value); + + public static implicit operator MaxValue(double value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of MaxValue"); + } + } + + public virtual bool Equals(MaxValue? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + _ => -1, + }; + } +} + +sealed class MaxValueConverter : JsonConverter +{ + public override MaxValue? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, MaxValue value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Minimum value of the field. Only set if field type is `Date` or `Number`. For +/// `Date` type field, the value will be in ISO8601 string format. For `Number` type +/// field, it will be a numeric value. +/// +[JsonConverter(typeof(MinValueConverter))] +public record class MinValue : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public MinValue(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public MinValue(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public MinValue(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @string, System::Action @double) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of MinValue" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @string, System::Func @double) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of MinValue" + ), + }; + } + + public static implicit operator MinValue(string value) => new(value); + + public static implicit operator MinValue(double value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of MinValue"); + } + } + + public virtual bool Equals(MinValue? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + _ => -1, + }; + } +} + +sealed class MinValueConverter : JsonConverter +{ + public override MinValue? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, MinValue value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(SelectOptionConverter))] +public record class SelectOption : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public SelectOption(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public SelectOption(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public SelectOption(bool value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public SelectOption(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickBool(out var value)) { + /// // `value` is of type `bool` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickBool([NotNullWhen(true)] out bool? value) + { + value = this.Value as bool?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action @double, + System::Action @bool + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case double value: + @double(value); + break; + case bool value: + @bool(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of SelectOption" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (double value) => {...}, + /// (bool value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func @double, + System::Func @bool + ) + { + return this.Value switch + { + string value => @string(value), + double value => @double(value), + bool value => @bool(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of SelectOption" + ), + }; + } + + public static implicit operator SelectOption(string value) => new(value); + + public static implicit operator SelectOption(double value) => new(value); + + public static implicit operator SelectOption(bool value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of SelectOption" + ); + } + } + + public virtual bool Equals(SelectOption? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + double _ => 1, + bool _ => 2, + _ => -1, + }; + } +} + +sealed class SelectOptionConverter : JsonConverter +{ + public override SelectOption? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + SelectOption value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} diff --git a/src/Imagekit/Models/SharedSavedExtension.cs b/src/Imagekit/Models/SharedSavedExtension.cs new file mode 100644 index 00000000..38daabfb --- /dev/null +++ b/src/Imagekit/Models/SharedSavedExtension.cs @@ -0,0 +1,190 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models; + +/// +/// Saved extension object containing extension configuration. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class SharedSavedExtension : JsonModel +{ + /// + /// Unique identifier of the saved extension. + /// + public string? ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("id"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("id", value); + } + } + + /// + /// Configuration object for an extension (base extensions only, not saved extension references). + /// + public ExtensionConfig? Config + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("config"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("config", value); + } + } + + /// + /// Timestamp when the saved extension was created. + /// + public DateTimeOffset? CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("createdAt"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("createdAt", value); + } + } + + /// + /// Description of the saved extension. + /// + public string? Description + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("description"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("description", value); + } + } + + /// + /// Name of the saved extension. + /// + public string? Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("name"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("name", value); + } + } + + /// + /// Timestamp when the saved extension was last updated. + /// + public DateTimeOffset? UpdatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("updatedAt"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("updatedAt", value); + } + } + + /// + public override void Validate() + { + _ = this.ID; + this.Config?.Validate(); + _ = this.CreatedAt; + _ = this.Description; + _ = this.Name; + _ = this.UpdatedAt; + } + + public SharedSavedExtension() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SharedSavedExtension(SharedSavedExtension sharedSavedExtension) + : base(sharedSavedExtension) { } +#pragma warning restore CS8618 + + public SharedSavedExtension(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SharedSavedExtension(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SharedSavedExtension FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class SharedSavedExtensionFromRaw : IFromRawJson +{ + /// + public SharedSavedExtension FromRawUnchecked( + IReadOnlyDictionary rawData + ) => SharedSavedExtension.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/SolidColorOverlay.cs b/src/Imagekit/Models/SolidColorOverlay.cs new file mode 100644 index 00000000..ffa89050 --- /dev/null +++ b/src/Imagekit/Models/SolidColorOverlay.cs @@ -0,0 +1,341 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; + +namespace Imagekit.Models; + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class SolidColorOverlay : JsonModel +{ + /// + /// Controls how the layer blends with the base image or underlying content. + /// Maps to `lm` in the URL. By default, layers completely cover the base image + /// beneath them. Layer modes change this behavior: - `multiply`: Multiplies + /// the pixel values of the layer with the base image. The result is always darker + /// than the original images. This is ideal for applying shadows or color tints. + /// - `displace`: Uses the layer as a displacement map to distort pixels in the + /// base image. The red channel controls horizontal displacement, and the green + /// channel controls vertical displacement. Requires `x` or `y` parameter to control + /// displacement magnitude. - `cutout`: Acts as an inverse mask where opaque areas + /// of the layer turn the base image transparent, while transparent areas leave + /// the base image unchanged. This mode functions like a hole-punch, effectively + /// cutting the shape of the layer out of the underlying image. - `cutter`: Acts + /// as a shape mask where only the parts of the base image that fall inside the + /// opaque area of the layer are preserved. This mode functions like a cookie-cutter, + /// trimming the base image to match the specific dimensions and shape of the + /// layer. See [Layer modes](https://imagekit.io/docs/add-overlays-on-images#layer-modes). + /// + public ApiEnum? LayerMode + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("layerMode"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("layerMode", value); + } + } + + public OverlayPosition? Position + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("position"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("position", value); + } + } + + public OverlayTiming? Timing + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("timing"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("timing", value); + } + } + + /// + /// Specifies the color of the block using an RGB hex code (e.g., `FF0000`), an + /// RGBA code (e.g., `FFAABB50`), or a color name (e.g., `red`). If an 8-character + /// value is provided, the last two characters represent the opacity level (from + /// `00` for 0.00 to `99` for 0.99). + /// + public required string Color + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("color"); + } + init { this._rawData.Set("color", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Control width and height of the solid color overlay. Supported transformations + /// depend on the base/parent asset. See overlays on [Images](https://imagekit.io/docs/add-overlays-on-images#apply-transformation-on-solid-color-overlay) + /// and [Videos](https://imagekit.io/docs/add-overlays-on-videos#apply-transformations-on-solid-color-block-overlay). + /// + public IReadOnlyList? Transformation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>( + "transformation" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "transformation", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + public static implicit operator BaseOverlay(SolidColorOverlay solidColorOverlay) => + new() + { + LayerMode = solidColorOverlay.LayerMode, + Position = solidColorOverlay.Position, + Timing = solidColorOverlay.Timing, + }; + + /// + public override void Validate() + { + this.LayerMode?.Validate(); + this.Position?.Validate(); + this.Timing?.Validate(); + _ = this.Color; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("solidColor"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + foreach (var item in this.Transformation ?? []) + { + item.Validate(); + } + } + + public SolidColorOverlay() + { + this.Type = JsonSerializer.SerializeToElement("solidColor"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SolidColorOverlay(SolidColorOverlay solidColorOverlay) + : base(solidColorOverlay) { } +#pragma warning restore CS8618 + + public SolidColorOverlay(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("solidColor"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SolidColorOverlay(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SolidColorOverlay FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public SolidColorOverlay(string color) + : this() + { + this.Color = color; + } +} + +class SolidColorOverlayFromRaw : IFromRawJson +{ + /// + public SolidColorOverlay FromRawUnchecked(IReadOnlyDictionary rawData) => + SolidColorOverlay.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter< + SolidColorOverlaySolidColorOverlay, + SolidColorOverlaySolidColorOverlayFromRaw + >) +)] +public sealed record class SolidColorOverlaySolidColorOverlay : JsonModel +{ + /// + /// Specifies the color of the block using an RGB hex code (e.g., `FF0000`), an + /// RGBA code (e.g., `FFAABB50`), or a color name (e.g., `red`). If an 8-character + /// value is provided, the last two characters represent the opacity level (from + /// `00` for 0.00 to `99` for 0.99). + /// + public required string Color + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("color"); + } + init { this._rawData.Set("color", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Control width and height of the solid color overlay. Supported transformations + /// depend on the base/parent asset. See overlays on [Images](https://imagekit.io/docs/add-overlays-on-images#apply-transformation-on-solid-color-overlay) + /// and [Videos](https://imagekit.io/docs/add-overlays-on-videos#apply-transformations-on-solid-color-block-overlay). + /// + public IReadOnlyList? Transformation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>( + "transformation" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "transformation", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.Color; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("solidColor"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + foreach (var item in this.Transformation ?? []) + { + item.Validate(); + } + } + + public SolidColorOverlaySolidColorOverlay() + { + this.Type = JsonSerializer.SerializeToElement("solidColor"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SolidColorOverlaySolidColorOverlay( + SolidColorOverlaySolidColorOverlay solidColorOverlaySolidColorOverlay + ) + : base(solidColorOverlaySolidColorOverlay) { } +#pragma warning restore CS8618 + + public SolidColorOverlaySolidColorOverlay(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("solidColor"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SolidColorOverlaySolidColorOverlay(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SolidColorOverlaySolidColorOverlay FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public SolidColorOverlaySolidColorOverlay(string color) + : this() + { + this.Color = color; + } +} + +class SolidColorOverlaySolidColorOverlayFromRaw : IFromRawJson +{ + /// + public SolidColorOverlaySolidColorOverlay FromRawUnchecked( + IReadOnlyDictionary rawData + ) => SolidColorOverlaySolidColorOverlay.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/SolidColorOverlayTransformation.cs b/src/Imagekit/Models/SolidColorOverlayTransformation.cs new file mode 100644 index 00000000..8caed393 --- /dev/null +++ b/src/Imagekit/Models/SolidColorOverlayTransformation.cs @@ -0,0 +1,1354 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models; + +[JsonConverter( + typeof(JsonModelConverter< + SolidColorOverlayTransformation, + SolidColorOverlayTransformationFromRaw + >) +)] +public sealed record class SolidColorOverlayTransformation : JsonModel +{ + /// + /// Specifies the transparency level of the overlaid solid color layer. Supports + /// integers from `1` to `9`. + /// + public double? Alpha + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("alpha"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("alpha", value); + } + } + + /// + /// Specifies the background color of the solid color overlay. Accepts an RGB + /// hex code (e.g., `FF0000`), an RGBA code (e.g., `FFAABB50`), or a color name. + /// + public string? Background + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("background"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("background", value); + } + } + + /// + /// Creates a linear gradient with two colors. Pass `true` for a default gradient, + /// or provide a string for a custom gradient. Only works if the base asset is + /// an image. See [gradient](https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient). + /// + public Gradient? Gradient + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("gradient"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("gradient", value); + } + } + + /// + /// Controls the height of the solid color overlay. Accepts a numeric value or + /// an arithmetic expression. Learn about [arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). + /// + public Height? Height + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("height"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("height", value); + } + } + + /// + /// Specifies the corner radius of the solid color overlay. - Single value (positive + /// integer): Applied to all corners (e.g., `20`). - `max`: Creates a circular + /// or oval shape. - Per-corner array: Provide four underscore-separated values + /// representing top-left, top-right, bottom-right, and bottom-left corners respectively + /// (e.g., `10_20_30_40`). See [Radius](https://imagekit.io/docs/effects-and-enhancements#radius---r). + /// + public Radius? Radius + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("radius"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("radius", value); + } + } + + /// + /// Controls the width of the solid color overlay. Accepts a numeric value or + /// an arithmetic expression (e.g., `bw_mul_0.2` or `bh_div_2`). Learn about + /// [arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). + /// + public Width? Width + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("width"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("width", value); + } + } + + /// + public override void Validate() + { + _ = this.Alpha; + _ = this.Background; + this.Gradient?.Validate(); + this.Height?.Validate(); + this.Radius?.Validate(); + this.Width?.Validate(); + } + + public SolidColorOverlayTransformation() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SolidColorOverlayTransformation( + SolidColorOverlayTransformation solidColorOverlayTransformation + ) + : base(solidColorOverlayTransformation) { } +#pragma warning restore CS8618 + + public SolidColorOverlayTransformation(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SolidColorOverlayTransformation(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SolidColorOverlayTransformation FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class SolidColorOverlayTransformationFromRaw : IFromRawJson +{ + /// + public SolidColorOverlayTransformation FromRawUnchecked( + IReadOnlyDictionary rawData + ) => SolidColorOverlayTransformation.FromRawUnchecked(rawData); +} + +/// +/// Creates a linear gradient with two colors. Pass `true` for a default gradient, +/// or provide a string for a custom gradient. Only works if the base asset is an +/// image. See [gradient](https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient). +/// +[JsonConverter(typeof(GradientConverter))] +public record class Gradient : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public Gradient(Default value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Gradient(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Gradient(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDefault(out var value)) { + /// // `value` is of type `Default` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDefault([NotNullWhen(true)] out Default? value) + { + value = this.Value as Default; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (Default value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action default_, System::Action @string) + { + switch (this.Value) + { + case Default value: + default_(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of Gradient" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (Default value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func default_, System::Func @string) + { + return this.Value switch + { + Default value => default_(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of Gradient" + ), + }; + } + + public static implicit operator Gradient(Default value) => new(value); + + public static implicit operator Gradient(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of Gradient"); + } + this.Switch((default_) => default_.Validate(), (_) => { }); + } + + public virtual bool Equals(Gradient? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + Default _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class GradientConverter : JsonConverter +{ + public override Gradient? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, Gradient value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(DefaultConverter))] +public record class Default +{ + public JsonElement Element { get; private init; } + + public Default() + { + Element = JsonSerializer.SerializeToElement(true); + } + + internal Default(JsonElement element) + { + Element = element; + } + + /// + /// Validates that the instance's underlying value is the expected constant. + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public void Validate() + { + if (this != new Default()) + { + throw new ImageKitInvalidDataException("Invalid value given for 'Default'"); + } + } + + public override int GetHashCode() + { + return 0; + } + + public virtual bool Equals(Default? other) + { + if (other == null) + { + return false; + } + + return JsonElement.DeepEquals(this.Element, other.Element); + } +} + +class DefaultConverter : JsonConverter +{ + public override Default? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return new(JsonSerializer.Deserialize(ref reader, options)); + } + + public override void Write(Utf8JsonWriter writer, Default value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Element, options); + } +} + +/// +/// Controls the height of the solid color overlay. Accepts a numeric value or an +/// arithmetic expression. Learn about [arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). +/// +[JsonConverter(typeof(HeightConverter))] +public record class Height : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public Height(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Height(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Height(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException("Data did not match any variant of Height"); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException("Data did not match any variant of Height"), + }; + } + + public static implicit operator Height(double value) => new(value); + + public static implicit operator Height(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of Height"); + } + } + + public virtual bool Equals(Height? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class HeightConverter : JsonConverter +{ + public override Height? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, Height value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Specifies the corner radius of the solid color overlay. - Single value (positive +/// integer): Applied to all corners (e.g., `20`). - `max`: Creates a circular or +/// oval shape. - Per-corner array: Provide four underscore-separated values representing +/// top-left, top-right, bottom-right, and bottom-left corners respectively (e.g., +/// `10_20_30_40`). See [Radius](https://imagekit.io/docs/effects-and-enhancements#radius---r). +/// +[JsonConverter(typeof(RadiusConverter))] +public record class Radius : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public Radius(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Radius(Max value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Radius(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Radius(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickMax(out var value)) { + /// // `value` is of type `Max` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickMax([NotNullWhen(true)] out Max? value) + { + value = this.Value as Max; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (Max value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @double, + System::Action max, + System::Action @string + ) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case Max value: + max(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException("Data did not match any variant of Radius"); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (Max value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @double, + System::Func max, + System::Func @string + ) + { + return this.Value switch + { + double value => @double(value), + Max value => max(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException("Data did not match any variant of Radius"), + }; + } + + public static implicit operator Radius(double value) => new(value); + + public static implicit operator Radius(Max value) => new(value); + + public static implicit operator Radius(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of Radius"); + } + this.Switch((_) => { }, (max) => max.Validate(), (_) => { }); + } + + public virtual bool Equals(Radius? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + Max _ => 1, + string _ => 2, + _ => -1, + }; + } +} + +sealed class RadiusConverter : JsonConverter +{ + public override Radius? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, Radius value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(MaxConverter))] +public record class Max +{ + public JsonElement Element { get; private init; } + + public Max() + { + Element = JsonSerializer.SerializeToElement("max"); + } + + internal Max(JsonElement element) + { + Element = element; + } + + /// + /// Validates that the instance's underlying value is the expected constant. + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public void Validate() + { + if (this != new Max()) + { + throw new ImageKitInvalidDataException("Invalid value given for 'Max'"); + } + } + + public override int GetHashCode() + { + return 0; + } + + public virtual bool Equals(Max? other) + { + if (other == null) + { + return false; + } + + return JsonElement.DeepEquals(this.Element, other.Element); + } +} + +class MaxConverter : JsonConverter +{ + public override Max? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return new(JsonSerializer.Deserialize(ref reader, options)); + } + + public override void Write(Utf8JsonWriter writer, Max value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Element, options); + } +} + +/// +/// Controls the width of the solid color overlay. Accepts a numeric value or an +/// arithmetic expression (e.g., `bw_mul_0.2` or `bh_div_2`). Learn about [arithmetic +/// expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). +/// +[JsonConverter(typeof(WidthConverter))] +public record class Width : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public Width(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Width(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Width(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException("Data did not match any variant of Width"); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException("Data did not match any variant of Width"), + }; + } + + public static implicit operator Width(double value) => new(value); + + public static implicit operator Width(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of Width"); + } + } + + public virtual bool Equals(Width? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class WidthConverter : JsonConverter +{ + public override Width? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, Width value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} diff --git a/src/Imagekit/Models/SrcOptions.cs b/src/Imagekit/Models/SrcOptions.cs new file mode 100644 index 00000000..0c1821b2 --- /dev/null +++ b/src/Imagekit/Models/SrcOptions.cs @@ -0,0 +1,231 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models; + +/// +/// Options for generating ImageKit URLs with transformations. See the [Transformations guide](https://imagekit.io/docs/transformations). +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class SrcOptions : JsonModel +{ + /// + /// Accepts a relative or absolute path of the resource. If a relative path is + /// provided, it is appended to the `urlEndpoint`. If an absolute path is provided, + /// `urlEndpoint` is ignored. + /// + public required string Src + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("src"); + } + init { this._rawData.Set("src", value); } + } + + /// + /// Get your urlEndpoint from the [ImageKit dashboard](https://imagekit.io/dashboard/url-endpoints). + /// + public required string UrlEndpoint + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("urlEndpoint"); + } + init { this._rawData.Set("urlEndpoint", value); } + } + + /// + /// When you want the signed URL to expire, specified in seconds. If `expiresIn` + /// is anything above 0, the URL will always be signed even if `signed` is set + /// to false. If not specified and `signed` is `true`, the signed URL will not + /// expire (valid indefinitely). + /// + /// Example: Setting `expiresIn: 3600` will make the URL expire 1 hour from + /// generation time. After the expiry time, the signed URL will no longer be valid + /// and ImageKit will return a 401 Unauthorized status code. + /// + /// [Learn more](https://imagekit.io/docs/media-delivery-basic-security#how-to-generate-signed-urls). + /// + public double? ExpiresIn + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("expiresIn"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("expiresIn", value); + } + } + + /// + /// These are additional query parameters that you want to add to the final URL. + /// They can be any query parameters and not necessarily related to ImageKit. + /// This is especially useful if you want to add a versioning parameter to your + /// URLs. + /// + public IReadOnlyDictionary? QueryParameters + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "queryParameters" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "queryParameters", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Whether to sign the URL or not. Set this to `true` if you want to generate + /// a signed URL. If `signed` is `true` and `expiresIn` is not specified, the + /// signed URL will not expire (valid indefinitely). Note: If `expiresIn` is + /// set to any value above 0, the URL will always be signed regardless of this + /// setting. [Learn more](https://imagekit.io/docs/media-delivery-basic-security#how-to-generate-signed-urls). + /// + public bool? Signed + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("signed"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("signed", value); + } + } + + /// + /// An array of objects specifying the transformations to be applied in the URL. + /// If more than one transformation is specified, they are applied in the order + /// they are specified as chained transformations. See [Chained transformations](https://imagekit.io/docs/transformations#chained-transformations). + /// + public IReadOnlyList? Transformation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>( + "transformation" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "transformation", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// By default, the transformation string is added as a query parameter in the + /// URL, e.g., `?tr=w-100,h-100`. If you want to add the transformation string + /// in the path of the URL, set this to `path`. Learn more in the [Transformations + /// guide](https://imagekit.io/docs/transformations). + /// + public ApiEnum? TransformationPosition + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "transformationPosition" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("transformationPosition", value); + } + } + + /// + public override void Validate() + { + _ = this.Src; + _ = this.UrlEndpoint; + _ = this.ExpiresIn; + _ = this.QueryParameters; + _ = this.Signed; + foreach (var item in this.Transformation ?? []) + { + item.Validate(); + } + this.TransformationPosition?.Validate(); + } + + public SrcOptions() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SrcOptions(SrcOptions srcOptions) + : base(srcOptions) { } +#pragma warning restore CS8618 + + public SrcOptions(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SrcOptions(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SrcOptions FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class SrcOptionsFromRaw : IFromRawJson +{ + /// + public SrcOptions FromRawUnchecked(IReadOnlyDictionary rawData) => + SrcOptions.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/StreamingResolution.cs b/src/Imagekit/Models/StreamingResolution.cs new file mode 100644 index 00000000..87b3620b --- /dev/null +++ b/src/Imagekit/Models/StreamingResolution.cs @@ -0,0 +1,68 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models; + +/// +/// Available streaming resolutions for [adaptive bitrate streaming](https://imagekit.io/docs/adaptive-bitrate-streaming) +/// +[JsonConverter(typeof(StreamingResolutionConverter))] +public enum StreamingResolution +{ + V240, + V360, + V480, + V720, + V1080, + V1440, + V2160, +} + +sealed class StreamingResolutionConverter : JsonConverter +{ + public override StreamingResolution Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "240" => StreamingResolution.V240, + "360" => StreamingResolution.V360, + "480" => StreamingResolution.V480, + "720" => StreamingResolution.V720, + "1080" => StreamingResolution.V1080, + "1440" => StreamingResolution.V1440, + "2160" => StreamingResolution.V2160, + _ => (StreamingResolution)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + StreamingResolution value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + StreamingResolution.V240 => "240", + StreamingResolution.V360 => "360", + StreamingResolution.V480 => "480", + StreamingResolution.V720 => "720", + StreamingResolution.V1080 => "1080", + StreamingResolution.V1440 => "1440", + StreamingResolution.V2160 => "2160", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} diff --git a/src/Imagekit/Models/SubtitleOverlay.cs b/src/Imagekit/Models/SubtitleOverlay.cs new file mode 100644 index 00000000..ac631253 --- /dev/null +++ b/src/Imagekit/Models/SubtitleOverlay.cs @@ -0,0 +1,449 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models; + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class SubtitleOverlay : JsonModel +{ + /// + /// Controls how the layer blends with the base image or underlying content. + /// Maps to `lm` in the URL. By default, layers completely cover the base image + /// beneath them. Layer modes change this behavior: - `multiply`: Multiplies + /// the pixel values of the layer with the base image. The result is always darker + /// than the original images. This is ideal for applying shadows or color tints. + /// - `displace`: Uses the layer as a displacement map to distort pixels in the + /// base image. The red channel controls horizontal displacement, and the green + /// channel controls vertical displacement. Requires `x` or `y` parameter to control + /// displacement magnitude. - `cutout`: Acts as an inverse mask where opaque areas + /// of the layer turn the base image transparent, while transparent areas leave + /// the base image unchanged. This mode functions like a hole-punch, effectively + /// cutting the shape of the layer out of the underlying image. - `cutter`: Acts + /// as a shape mask where only the parts of the base image that fall inside the + /// opaque area of the layer are preserved. This mode functions like a cookie-cutter, + /// trimming the base image to match the specific dimensions and shape of the + /// layer. See [Layer modes](https://imagekit.io/docs/add-overlays-on-images#layer-modes). + /// + public ApiEnum? LayerMode + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("layerMode"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("layerMode", value); + } + } + + public OverlayPosition? Position + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("position"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("position", value); + } + } + + public OverlayTiming? Timing + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("timing"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("timing", value); + } + } + + /// + /// Specifies the relative path to the subtitle file used as an overlay. + /// + public required string Input + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("input"); + } + init { this._rawData.Set("input", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// The input path can be included in the layer as either `i-{input}` or `ie-{base64_encoded_input}`. + /// By default, the SDK determines the appropriate format automatically. To + /// always use base64 encoding (`ie-{base64}`), set this parameter to `base64`. + /// To always use plain text (`i-{input}`), set it to `plain`. + /// + /// Regardless of the encoding method: - Leading and trailing slashes are + /// removed. - Remaining slashes within the path are replaced with `@@` when using + /// plain text. + /// + public ApiEnum? Encoding + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum + >("encoding"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("encoding", value); + } + } + + /// + /// Control styling of the subtitle. See [Styling subtitles](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer). + /// + public IReadOnlyList? Transformation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>( + "transformation" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "transformation", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + public static implicit operator BaseOverlay(SubtitleOverlay subtitleOverlay) => + new() + { + LayerMode = subtitleOverlay.LayerMode, + Position = subtitleOverlay.Position, + Timing = subtitleOverlay.Timing, + }; + + /// + public override void Validate() + { + this.LayerMode?.Validate(); + this.Position?.Validate(); + this.Timing?.Validate(); + _ = this.Input; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("subtitle"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + this.Encoding?.Validate(); + foreach (var item in this.Transformation ?? []) + { + item.Validate(); + } + } + + public SubtitleOverlay() + { + this.Type = JsonSerializer.SerializeToElement("subtitle"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SubtitleOverlay(SubtitleOverlay subtitleOverlay) + : base(subtitleOverlay) { } +#pragma warning restore CS8618 + + public SubtitleOverlay(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("subtitle"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SubtitleOverlay(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SubtitleOverlay FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public SubtitleOverlay(string input) + : this() + { + this.Input = input; + } +} + +class SubtitleOverlayFromRaw : IFromRawJson +{ + /// + public SubtitleOverlay FromRawUnchecked(IReadOnlyDictionary rawData) => + SubtitleOverlay.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter< + SubtitleOverlaySubtitleOverlay, + SubtitleOverlaySubtitleOverlayFromRaw + >) +)] +public sealed record class SubtitleOverlaySubtitleOverlay : JsonModel +{ + /// + /// Specifies the relative path to the subtitle file used as an overlay. + /// + public required string Input + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("input"); + } + init { this._rawData.Set("input", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// The input path can be included in the layer as either `i-{input}` or `ie-{base64_encoded_input}`. + /// By default, the SDK determines the appropriate format automatically. To + /// always use base64 encoding (`ie-{base64}`), set this parameter to `base64`. + /// To always use plain text (`i-{input}`), set it to `plain`. + /// + /// Regardless of the encoding method: - Leading and trailing slashes are + /// removed. - Remaining slashes within the path are replaced with `@@` when using + /// plain text. + /// + public ApiEnum? Encoding + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum + >("encoding"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("encoding", value); + } + } + + /// + /// Control styling of the subtitle. See [Styling subtitles](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer). + /// + public IReadOnlyList? Transformation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>( + "transformation" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "transformation", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.Input; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("subtitle"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + this.Encoding?.Validate(); + foreach (var item in this.Transformation ?? []) + { + item.Validate(); + } + } + + public SubtitleOverlaySubtitleOverlay() + { + this.Type = JsonSerializer.SerializeToElement("subtitle"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SubtitleOverlaySubtitleOverlay( + SubtitleOverlaySubtitleOverlay subtitleOverlaySubtitleOverlay + ) + : base(subtitleOverlaySubtitleOverlay) { } +#pragma warning restore CS8618 + + public SubtitleOverlaySubtitleOverlay(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("subtitle"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SubtitleOverlaySubtitleOverlay(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SubtitleOverlaySubtitleOverlay FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public SubtitleOverlaySubtitleOverlay(string input) + : this() + { + this.Input = input; + } +} + +class SubtitleOverlaySubtitleOverlayFromRaw : IFromRawJson +{ + /// + public SubtitleOverlaySubtitleOverlay FromRawUnchecked( + IReadOnlyDictionary rawData + ) => SubtitleOverlaySubtitleOverlay.FromRawUnchecked(rawData); +} + +/// +/// The input path can be included in the layer as either `i-{input}` or `ie-{base64_encoded_input}`. +/// By default, the SDK determines the appropriate format automatically. To always +/// use base64 encoding (`ie-{base64}`), set this parameter to `base64`. To always +/// use plain text (`i-{input}`), set it to `plain`. +/// +/// Regardless of the encoding method: - Leading and trailing slashes are removed. +/// - Remaining slashes within the path are replaced with `@@` when using plain text. +/// +[JsonConverter(typeof(SubtitleOverlaySubtitleOverlayEncodingConverter))] +public enum SubtitleOverlaySubtitleOverlayEncoding +{ + Auto, + Plain, + Base64, +} + +sealed class SubtitleOverlaySubtitleOverlayEncodingConverter + : JsonConverter +{ + public override SubtitleOverlaySubtitleOverlayEncoding Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "auto" => SubtitleOverlaySubtitleOverlayEncoding.Auto, + "plain" => SubtitleOverlaySubtitleOverlayEncoding.Plain, + "base64" => SubtitleOverlaySubtitleOverlayEncoding.Base64, + _ => (SubtitleOverlaySubtitleOverlayEncoding)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + SubtitleOverlaySubtitleOverlayEncoding value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + SubtitleOverlaySubtitleOverlayEncoding.Auto => "auto", + SubtitleOverlaySubtitleOverlayEncoding.Plain => "plain", + SubtitleOverlaySubtitleOverlayEncoding.Base64 => "base64", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} diff --git a/src/Imagekit/Models/SubtitleOverlayTransformation.cs b/src/Imagekit/Models/SubtitleOverlayTransformation.cs new file mode 100644 index 00000000..3b07710c --- /dev/null +++ b/src/Imagekit/Models/SubtitleOverlayTransformation.cs @@ -0,0 +1,296 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models; + +/// +/// Subtitle styling options. [Learn more](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) +/// from the docs. +/// +[JsonConverter( + typeof(JsonModelConverter) +)] +public sealed record class SubtitleOverlayTransformation : JsonModel +{ + /// + /// Specifies the subtitle background color using a standard color name, an RGB + /// color code (e.g., FF0000), or an RGBA color code (e.g., FFAABB50). + /// + /// [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + /// + public string? Background + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("background"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("background", value); + } + } + + /// + /// Sets the font color of the subtitle text using a standard color name, an + /// RGB color code (e.g., FF0000), or an RGBA color code (e.g., FFAABB50). + /// + /// [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + /// + public string? Color + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("color"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("color", value); + } + } + + /// + /// Sets the font family of subtitle text. Refer to the [supported fonts documented](https://imagekit.io/docs/add-overlays-on-images#supported-text-font-list) + /// in the ImageKit transformations guide. + /// + public string? FontFamily + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("fontFamily"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("fontFamily", value); + } + } + + /// + /// Sets the font outline of the subtitle text. Requires the outline width (an + /// integer) and the outline color (as an RGB color code, RGBA color code, or + /// standard web color name) separated by an underscore. Example: `fol-2_blue` + /// (outline width of 2px and outline color blue), `fol-2_A1CCDD` (outline width + /// of 2px and outline color `#A1CCDD`) and `fol-2_A1CCDD50` (outline width of + /// 2px and outline color `#A1CCDD` at 50% opacity). + /// + /// [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + /// + public string? FontOutline + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("fontOutline"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("fontOutline", value); + } + } + + /// + /// Sets the font shadow for the subtitle text. Requires the shadow color (as + /// an RGB color code, RGBA color code, or standard web color name) and shadow + /// indent (an integer) separated by an underscore. Example: `fsh-blue_2` (shadow + /// color blue, indent of 2px), `fsh-A1CCDD_3` (shadow color `#A1CCDD`, indent + /// of 3px), `fsh-A1CCDD50_3` (shadow color `#A1CCDD` at 50% opacity, indent of 3px). + /// + /// [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + /// + public string? FontShadow + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("fontShadow"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("fontShadow", value); + } + } + + /// + /// Sets the font size of subtitle text. + /// + /// [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + /// + public double? FontSize + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("fontSize"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("fontSize", value); + } + } + + /// + /// Sets the typography style of the subtitle text. Supports values are `b` for + /// bold, `i` for italics, and `b_i` for bold with italics. + /// + /// [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + /// + public ApiEnum? Typography + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("typography"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("typography", value); + } + } + + /// + public override void Validate() + { + _ = this.Background; + _ = this.Color; + _ = this.FontFamily; + _ = this.FontOutline; + _ = this.FontShadow; + _ = this.FontSize; + this.Typography?.Validate(); + } + + public SubtitleOverlayTransformation() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SubtitleOverlayTransformation( + SubtitleOverlayTransformation subtitleOverlayTransformation + ) + : base(subtitleOverlayTransformation) { } +#pragma warning restore CS8618 + + public SubtitleOverlayTransformation(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SubtitleOverlayTransformation(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SubtitleOverlayTransformation FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class SubtitleOverlayTransformationFromRaw : IFromRawJson +{ + /// + public SubtitleOverlayTransformation FromRawUnchecked( + IReadOnlyDictionary rawData + ) => SubtitleOverlayTransformation.FromRawUnchecked(rawData); +} + +/// +/// Sets the typography style of the subtitle text. Supports values are `b` for bold, +/// `i` for italics, and `b_i` for bold with italics. +/// +/// [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) +/// +[JsonConverter(typeof(TypographyConverter))] +public enum Typography +{ + B, + I, + BI, +} + +sealed class TypographyConverter : JsonConverter +{ + public override Typography Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "b" => Typography.B, + "i" => Typography.I, + "b_i" => Typography.BI, + _ => (Typography)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + Typography value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + Typography.B => "b", + Typography.I => "i", + Typography.BI => "b_i", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} diff --git a/src/Imagekit/Models/TextOverlay.cs b/src/Imagekit/Models/TextOverlay.cs new file mode 100644 index 00000000..d46e5f81 --- /dev/null +++ b/src/Imagekit/Models/TextOverlay.cs @@ -0,0 +1,441 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models; + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class TextOverlay : JsonModel +{ + /// + /// Controls how the layer blends with the base image or underlying content. + /// Maps to `lm` in the URL. By default, layers completely cover the base image + /// beneath them. Layer modes change this behavior: - `multiply`: Multiplies + /// the pixel values of the layer with the base image. The result is always darker + /// than the original images. This is ideal for applying shadows or color tints. + /// - `displace`: Uses the layer as a displacement map to distort pixels in the + /// base image. The red channel controls horizontal displacement, and the green + /// channel controls vertical displacement. Requires `x` or `y` parameter to control + /// displacement magnitude. - `cutout`: Acts as an inverse mask where opaque areas + /// of the layer turn the base image transparent, while transparent areas leave + /// the base image unchanged. This mode functions like a hole-punch, effectively + /// cutting the shape of the layer out of the underlying image. - `cutter`: Acts + /// as a shape mask where only the parts of the base image that fall inside the + /// opaque area of the layer are preserved. This mode functions like a cookie-cutter, + /// trimming the base image to match the specific dimensions and shape of the + /// layer. See [Layer modes](https://imagekit.io/docs/add-overlays-on-images#layer-modes). + /// + public ApiEnum? LayerMode + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("layerMode"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("layerMode", value); + } + } + + public OverlayPosition? Position + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("position"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("position", value); + } + } + + public OverlayTiming? Timing + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("timing"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("timing", value); + } + } + + /// + /// Specifies the text to be displayed in the overlay. The SDK automatically + /// handles special characters and encoding. + /// + public required string Text + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("text"); + } + init { this._rawData.Set("text", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Text can be included in the layer as either `i-{input}` (plain text) or `ie-{base64_encoded_input}` + /// (base64). By default, the SDK selects the appropriate format based on the + /// input text. To always use base64 (`ie-{base64}`), set this parameter to `base64`. + /// To always use plain text (`i-{input}`), set it to `plain`. + /// + /// Regardless of the encoding method, the input text is always percent-encoded + /// to ensure it is URL-safe. + /// + public ApiEnum? Encoding + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "encoding" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("encoding", value); + } + } + + /// + /// Control styling of the text overlay. See [Text overlays](https://imagekit.io/docs/add-overlays-on-images#text-overlay). + /// + public IReadOnlyList? Transformation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>( + "transformation" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "transformation", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + public static implicit operator BaseOverlay(TextOverlay textOverlay) => + new() + { + LayerMode = textOverlay.LayerMode, + Position = textOverlay.Position, + Timing = textOverlay.Timing, + }; + + /// + public override void Validate() + { + this.LayerMode?.Validate(); + this.Position?.Validate(); + this.Timing?.Validate(); + _ = this.Text; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("text"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + this.Encoding?.Validate(); + foreach (var item in this.Transformation ?? []) + { + item.Validate(); + } + } + + public TextOverlay() + { + this.Type = JsonSerializer.SerializeToElement("text"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public TextOverlay(TextOverlay textOverlay) + : base(textOverlay) { } +#pragma warning restore CS8618 + + public TextOverlay(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("text"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + TextOverlay(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static TextOverlay FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public TextOverlay(string text) + : this() + { + this.Text = text; + } +} + +class TextOverlayFromRaw : IFromRawJson +{ + /// + public TextOverlay FromRawUnchecked(IReadOnlyDictionary rawData) => + TextOverlay.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class TextOverlayTextOverlay : JsonModel +{ + /// + /// Specifies the text to be displayed in the overlay. The SDK automatically + /// handles special characters and encoding. + /// + public required string Text + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("text"); + } + init { this._rawData.Set("text", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Text can be included in the layer as either `i-{input}` (plain text) or `ie-{base64_encoded_input}` + /// (base64). By default, the SDK selects the appropriate format based on the + /// input text. To always use base64 (`ie-{base64}`), set this parameter to `base64`. + /// To always use plain text (`i-{input}`), set it to `plain`. + /// + /// Regardless of the encoding method, the input text is always percent-encoded + /// to ensure it is URL-safe. + /// + public ApiEnum? Encoding + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "encoding" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("encoding", value); + } + } + + /// + /// Control styling of the text overlay. See [Text overlays](https://imagekit.io/docs/add-overlays-on-images#text-overlay). + /// + public IReadOnlyList? Transformation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>( + "transformation" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "transformation", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.Text; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("text"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + this.Encoding?.Validate(); + foreach (var item in this.Transformation ?? []) + { + item.Validate(); + } + } + + public TextOverlayTextOverlay() + { + this.Type = JsonSerializer.SerializeToElement("text"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public TextOverlayTextOverlay(TextOverlayTextOverlay textOverlayTextOverlay) + : base(textOverlayTextOverlay) { } +#pragma warning restore CS8618 + + public TextOverlayTextOverlay(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("text"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + TextOverlayTextOverlay(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static TextOverlayTextOverlay FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public TextOverlayTextOverlay(string text) + : this() + { + this.Text = text; + } +} + +class TextOverlayTextOverlayFromRaw : IFromRawJson +{ + /// + public TextOverlayTextOverlay FromRawUnchecked( + IReadOnlyDictionary rawData + ) => TextOverlayTextOverlay.FromRawUnchecked(rawData); +} + +/// +/// Text can be included in the layer as either `i-{input}` (plain text) or `ie-{base64_encoded_input}` +/// (base64). By default, the SDK selects the appropriate format based on the input +/// text. To always use base64 (`ie-{base64}`), set this parameter to `base64`. +/// To always use plain text (`i-{input}`), set it to `plain`. +/// +/// Regardless of the encoding method, the input text is always percent-encoded +/// to ensure it is URL-safe. +/// +[JsonConverter(typeof(TextOverlayTextOverlayEncodingConverter))] +public enum TextOverlayTextOverlayEncoding +{ + Auto, + Plain, + Base64, +} + +sealed class TextOverlayTextOverlayEncodingConverter : JsonConverter +{ + public override TextOverlayTextOverlayEncoding Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "auto" => TextOverlayTextOverlayEncoding.Auto, + "plain" => TextOverlayTextOverlayEncoding.Plain, + "base64" => TextOverlayTextOverlayEncoding.Base64, + _ => (TextOverlayTextOverlayEncoding)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + TextOverlayTextOverlayEncoding value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + TextOverlayTextOverlayEncoding.Auto => "auto", + TextOverlayTextOverlayEncoding.Plain => "plain", + TextOverlayTextOverlayEncoding.Base64 => "base64", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} diff --git a/src/Imagekit/Models/TextOverlayTransformation.cs b/src/Imagekit/Models/TextOverlayTransformation.cs new file mode 100644 index 00000000..65cbf015 --- /dev/null +++ b/src/Imagekit/Models/TextOverlayTransformation.cs @@ -0,0 +1,2070 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models; + +[JsonConverter( + typeof(JsonModelConverter) +)] +public sealed record class TextOverlayTransformation : JsonModel +{ + /// + /// Specifies the transparency level of the text overlay. Accepts integers from + /// `1` to `9`. + /// + public double? Alpha + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("alpha"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("alpha", value); + } + } + + /// + /// Specifies the background color of the text overlay. Accepts an RGB hex code, + /// an RGBA code, or a color name. + /// + public string? Background + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("background"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("background", value); + } + } + + /// + /// Flip/mirror the text horizontally, vertically, or in both directions. Acceptable + /// values: `h` (horizontal), `v` (vertical), `h_v` (horizontal and vertical), + /// or `v_h`. + /// + public ApiEnum? Flip + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("flip"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("flip", value); + } + } + + /// + /// Specifies the font color of the overlaid text. Accepts an RGB hex code (e.g., + /// `FF0000`), an RGBA code (e.g., `FFAABB50`), or a color name. + /// + public string? FontColor + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("fontColor"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("fontColor", value); + } + } + + /// + /// Specifies the font family of the overlaid text. Choose from the supported + /// fonts list or use a custom font. See [Supported fonts](https://imagekit.io/docs/add-overlays-on-images#supported-text-font-list) + /// and [Custom font](https://imagekit.io/docs/add-overlays-on-images#change-font-family-in-text-overlay). + /// + public string? FontFamily + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("fontFamily"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("fontFamily", value); + } + } + + /// + /// Specifies the font size of the overlaid text. Accepts a numeric value or an + /// arithmetic expression. + /// + public FontSize? FontSize + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("fontSize"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("fontSize", value); + } + } + + /// + /// Specifies the inner alignment of the text when width is more than the text + /// length. + /// + public ApiEnum? InnerAlignment + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "innerAlignment" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("innerAlignment", value); + } + } + + /// + /// Specifies the line height for multi-line text overlays. It will come into + /// effect only if the text wraps over multiple lines. Accepts either an integer + /// value or an arithmetic expression. + /// + public LineHeight? LineHeight + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("lineHeight"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("lineHeight", value); + } + } + + /// + /// Specifies the padding around the overlaid text. Can be provided as a single + /// positive integer or multiple values separated by underscores (following CSS + /// shorthand order). Arithmetic expressions are also accepted. + /// + public Padding? Padding + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("padding"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("padding", value); + } + } + + /// + /// Specifies the corner radius: - Single value (positive integer): Applied to + /// all corners (e.g., `20`). - `max`: Creates a circular or oval shape. - Per-corner + /// array: Provide four underscore-separated values representing top-left, top-right, + /// bottom-right, and bottom-left corners respectively (e.g., `10_20_30_40`). + /// See [Radius](https://imagekit.io/docs/effects-and-enhancements#radius---r). + /// + public TextOverlayTransformationRadius? Radius + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("radius"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("radius", value); + } + } + + /// + /// Specifies the rotation angle of the text overlay. Accepts a numeric value + /// for clockwise rotation or a string prefixed with "N" for counter-clockwise + /// rotation. + /// + public Rotation? Rotation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("rotation"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("rotation", value); + } + } + + /// + /// Specifies the typography style of the text. Supported values: - Single + /// styles: `b` (bold), `i` (italic), `strikethrough`. - Combinations: Any + /// combination separated by underscores, e.g., `b_i`, `b_i_strikethrough`. + /// + public string? Typography + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("typography"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("typography", value); + } + } + + /// + /// Specifies the maximum width (in pixels) of the overlaid text. The text wraps + /// automatically, and arithmetic expressions (e.g., `bw_mul_0.2` or `bh_div_2`) + /// are supported. Useful when used in conjunction with the `background`. Learn + /// about [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). + /// + public TextOverlayTransformationWidth? Width + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("width"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("width", value); + } + } + + /// + public override void Validate() + { + _ = this.Alpha; + _ = this.Background; + this.Flip?.Validate(); + _ = this.FontColor; + _ = this.FontFamily; + this.FontSize?.Validate(); + this.InnerAlignment?.Validate(); + this.LineHeight?.Validate(); + this.Padding?.Validate(); + this.Radius?.Validate(); + this.Rotation?.Validate(); + _ = this.Typography; + this.Width?.Validate(); + } + + public TextOverlayTransformation() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public TextOverlayTransformation(TextOverlayTransformation textOverlayTransformation) + : base(textOverlayTransformation) { } +#pragma warning restore CS8618 + + public TextOverlayTransformation(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + TextOverlayTransformation(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static TextOverlayTransformation FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class TextOverlayTransformationFromRaw : IFromRawJson +{ + /// + public TextOverlayTransformation FromRawUnchecked( + IReadOnlyDictionary rawData + ) => TextOverlayTransformation.FromRawUnchecked(rawData); +} + +/// +/// Flip/mirror the text horizontally, vertically, or in both directions. Acceptable +/// values: `h` (horizontal), `v` (vertical), `h_v` (horizontal and vertical), or +/// `v_h`. +/// +[JsonConverter(typeof(FlipConverter))] +public enum Flip +{ + H, + V, + HV, + VH, +} + +sealed class FlipConverter : JsonConverter +{ + public override Flip Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "h" => Flip.H, + "v" => Flip.V, + "h_v" => Flip.HV, + "v_h" => Flip.VH, + _ => (Flip)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, Flip value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + Flip.H => "h", + Flip.V => "v", + Flip.HV => "h_v", + Flip.VH => "v_h", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Specifies the font size of the overlaid text. Accepts a numeric value or an arithmetic expression. +/// +[JsonConverter(typeof(FontSizeConverter))] +public record class FontSize : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public FontSize(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public FontSize(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public FontSize(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of FontSize" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of FontSize" + ), + }; + } + + public static implicit operator FontSize(double value) => new(value); + + public static implicit operator FontSize(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of FontSize"); + } + } + + public virtual bool Equals(FontSize? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class FontSizeConverter : JsonConverter +{ + public override FontSize? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, FontSize value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Specifies the inner alignment of the text when width is more than the text length. +/// +[JsonConverter(typeof(InnerAlignmentConverter))] +public enum InnerAlignment +{ + Left, + Right, + Center, +} + +sealed class InnerAlignmentConverter : JsonConverter +{ + public override InnerAlignment Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "left" => InnerAlignment.Left, + "right" => InnerAlignment.Right, + "center" => InnerAlignment.Center, + _ => (InnerAlignment)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + InnerAlignment value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + InnerAlignment.Left => "left", + InnerAlignment.Right => "right", + InnerAlignment.Center => "center", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Specifies the line height for multi-line text overlays. It will come into effect +/// only if the text wraps over multiple lines. Accepts either an integer value or +/// an arithmetic expression. +/// +[JsonConverter(typeof(LineHeightConverter))] +public record class LineHeight : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public LineHeight(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public LineHeight(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public LineHeight(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of LineHeight" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of LineHeight" + ), + }; + } + + public static implicit operator LineHeight(double value) => new(value); + + public static implicit operator LineHeight(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of LineHeight"); + } + } + + public virtual bool Equals(LineHeight? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class LineHeightConverter : JsonConverter +{ + public override LineHeight? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + LineHeight value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Specifies the padding around the overlaid text. Can be provided as a single positive +/// integer or multiple values separated by underscores (following CSS shorthand +/// order). Arithmetic expressions are also accepted. +/// +[JsonConverter(typeof(PaddingConverter))] +public record class Padding : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public Padding(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Padding(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Padding(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException("Data did not match any variant of Padding"); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of Padding" + ), + }; + } + + public static implicit operator Padding(double value) => new(value); + + public static implicit operator Padding(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of Padding"); + } + } + + public virtual bool Equals(Padding? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class PaddingConverter : JsonConverter +{ + public override Padding? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, Padding value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Specifies the corner radius: - Single value (positive integer): Applied to all +/// corners (e.g., `20`). - `max`: Creates a circular or oval shape. - Per-corner +/// array: Provide four underscore-separated values representing top-left, top-right, +/// bottom-right, and bottom-left corners respectively (e.g., `10_20_30_40`). See +/// [Radius](https://imagekit.io/docs/effects-and-enhancements#radius---r). +/// +[JsonConverter(typeof(TextOverlayTransformationRadiusConverter))] +public record class TextOverlayTransformationRadius : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public TextOverlayTransformationRadius(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TextOverlayTransformationRadius( + TextOverlayTransformationRadiusMax value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public TextOverlayTransformationRadius(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TextOverlayTransformationRadius(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickMax(out var value)) { + /// // `value` is of type `TextOverlayTransformationRadiusMax` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickMax([NotNullWhen(true)] out TextOverlayTransformationRadiusMax? value) + { + value = this.Value as TextOverlayTransformationRadiusMax; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (TextOverlayTransformationRadiusMax value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @double, + System::Action max, + System::Action @string + ) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case TextOverlayTransformationRadiusMax value: + max(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of TextOverlayTransformationRadius" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (TextOverlayTransformationRadiusMax value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @double, + System::Func max, + System::Func @string + ) + { + return this.Value switch + { + double value => @double(value), + TextOverlayTransformationRadiusMax value => max(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of TextOverlayTransformationRadius" + ), + }; + } + + public static implicit operator TextOverlayTransformationRadius(double value) => new(value); + + public static implicit operator TextOverlayTransformationRadius( + TextOverlayTransformationRadiusMax value + ) => new(value); + + public static implicit operator TextOverlayTransformationRadius(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of TextOverlayTransformationRadius" + ); + } + this.Switch((_) => { }, (max) => max.Validate(), (_) => { }); + } + + public virtual bool Equals(TextOverlayTransformationRadius? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + TextOverlayTransformationRadiusMax _ => 1, + string _ => 2, + _ => -1, + }; + } +} + +sealed class TextOverlayTransformationRadiusConverter + : JsonConverter +{ + public override TextOverlayTransformationRadius? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + TextOverlayTransformationRadius value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(TextOverlayTransformationRadiusMaxConverter))] +public record class TextOverlayTransformationRadiusMax +{ + public JsonElement Element { get; private init; } + + public TextOverlayTransformationRadiusMax() + { + Element = JsonSerializer.SerializeToElement("max"); + } + + internal TextOverlayTransformationRadiusMax(JsonElement element) + { + Element = element; + } + + /// + /// Validates that the instance's underlying value is the expected constant. + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public void Validate() + { + if (this != new TextOverlayTransformationRadiusMax()) + { + throw new ImageKitInvalidDataException( + "Invalid value given for 'TextOverlayTransformationRadiusMax'" + ); + } + } + + public override int GetHashCode() + { + return 0; + } + + public virtual bool Equals(TextOverlayTransformationRadiusMax? other) + { + if (other == null) + { + return false; + } + + return JsonElement.DeepEquals(this.Element, other.Element); + } +} + +class TextOverlayTransformationRadiusMaxConverter + : JsonConverter +{ + public override TextOverlayTransformationRadiusMax? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return new(JsonSerializer.Deserialize(ref reader, options)); + } + + public override void Write( + Utf8JsonWriter writer, + TextOverlayTransformationRadiusMax value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Element, options); + } +} + +/// +/// Specifies the rotation angle of the text overlay. Accepts a numeric value for +/// clockwise rotation or a string prefixed with "N" for counter-clockwise rotation. +/// +[JsonConverter(typeof(RotationConverter))] +public record class Rotation : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public Rotation(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Rotation(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Rotation(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of Rotation" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of Rotation" + ), + }; + } + + public static implicit operator Rotation(double value) => new(value); + + public static implicit operator Rotation(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of Rotation"); + } + } + + public virtual bool Equals(Rotation? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class RotationConverter : JsonConverter +{ + public override Rotation? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, Rotation value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Specifies the maximum width (in pixels) of the overlaid text. The text wraps automatically, +/// and arithmetic expressions (e.g., `bw_mul_0.2` or `bh_div_2`) are supported. Useful +/// when used in conjunction with the `background`. Learn about [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). +/// +[JsonConverter(typeof(TextOverlayTransformationWidthConverter))] +public record class TextOverlayTransformationWidth : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public TextOverlayTransformationWidth(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TextOverlayTransformationWidth(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TextOverlayTransformationWidth(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of TextOverlayTransformationWidth" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of TextOverlayTransformationWidth" + ), + }; + } + + public static implicit operator TextOverlayTransformationWidth(double value) => new(value); + + public static implicit operator TextOverlayTransformationWidth(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of TextOverlayTransformationWidth" + ); + } + } + + public virtual bool Equals(TextOverlayTransformationWidth? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class TextOverlayTransformationWidthConverter : JsonConverter +{ + public override TextOverlayTransformationWidth? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + TextOverlayTransformationWidth value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} diff --git a/src/Imagekit/Models/Transformation.cs b/src/Imagekit/Models/Transformation.cs new file mode 100644 index 00000000..bc725c60 --- /dev/null +++ b/src/Imagekit/Models/Transformation.cs @@ -0,0 +1,7294 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models; + +/// +/// The SDK provides easy-to-use names for transformations. These names are converted +/// to the corresponding transformation string before being added to the URL. SDKs +/// are updated regularly to support new transformations. If you want to use a transformation +/// that is not supported by the SDK, You can use the `raw` parameter to pass the +/// transformation string directly. See the [Transformations documentation](https://imagekit.io/docs/transformations). +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Transformation : JsonModel +{ + /// + /// Uses AI to change the background. Provide a text prompt or a base64-encoded + /// prompt, e.g., `prompt-snow road` or `prompte-[urlencoded_base64_encoded_text]`. + /// Not supported inside overlay. See [AI Change Background](https://imagekit.io/docs/ai-transformations#change-background-e-changebg). + /// + public string? AIChangeBackground + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("aiChangeBackground"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("aiChangeBackground", value); + } + } + + /// + /// Adds an AI-based drop shadow around a foreground object on a transparent + /// or removed background. Optionally, control the direction, elevation, and + /// saturation of the light source (e.g., `az-45` to change light direction). + /// Pass `true` for the default drop shadow, or provide a string for a custom + /// drop shadow. Supported inside overlay. See [AI Drop Shadow](https://imagekit.io/docs/ai-transformations#ai-drop-shadow-e-dropshadow). + /// + public AIDropShadow? AIDropShadow + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("aiDropShadow"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("aiDropShadow", value); + } + } + + /// + /// Uses AI to edit images based on a text prompt. Provide a text prompt or a + /// base64-encoded prompt, e.g., `prompt-snow road` or `prompte-[urlencoded_base64_encoded_text]`. + /// Not supported inside overlay. See [AI Edit](https://imagekit.io/docs/ai-transformations#edit-image-e-edit). + /// + public string? AIEdit + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("aiEdit"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("aiEdit", value); + } + } + + /// + /// Applies ImageKit's in-house background removal. Supported inside overlay. + /// See [AI Background Removal](https://imagekit.io/docs/ai-transformations#imagekit-background-removal-e-bgremove). + /// + public ApiEnum? AIRemoveBackground + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "aiRemoveBackground" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("aiRemoveBackground", value); + } + } + + /// + /// Uses third-party background removal. Note: It is recommended to use aiRemoveBackground, + /// ImageKit's in-house solution, which is more cost-effective. Supported inside + /// overlay. See [External Background Removal](https://imagekit.io/docs/ai-transformations#background-removal-e-removedotbg). + /// + public ApiEnum? AIRemoveBackgroundExternal + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "aiRemoveBackgroundExternal" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("aiRemoveBackgroundExternal", value); + } + } + + /// + /// Performs AI-based retouching to improve faces or product shots. Not supported + /// inside overlay. See [AI Retouch](https://imagekit.io/docs/ai-transformations#retouch-e-retouch). + /// + public ApiEnum? AIRetouch + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("aiRetouch"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("aiRetouch", value); + } + } + + /// + /// Upscales images beyond their original dimensions using AI. Not supported + /// inside overlay. See [AI Upscale](https://imagekit.io/docs/ai-transformations#upscale-e-upscale). + /// + public ApiEnum? AIUpscale + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("aiUpscale"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("aiUpscale", value); + } + } + + /// + /// Generates a variation of an image using AI. This produces a new image with + /// slight variations from the original, such as changes in color, texture, and + /// other visual elements, while preserving the structure and essence of the original + /// image. Not supported inside overlay. See [AI Generate Variations](https://imagekit.io/docs/ai-transformations#generate-variations-of-an-image-e-genvar). + /// + public ApiEnum? AIVariation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("aiVariation"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("aiVariation", value); + } + } + + /// + /// Specifies the aspect ratio for the output, e.g., "ar-4-3". Typically used + /// with either width or height (but not both). For example: aspectRatio = `4:3`, + /// `4_3`, or an expression like `iar_div_2`. See [Image resize and crop – Aspect + /// ratio](https://imagekit.io/docs/image-resize-and-crop#aspect-ratio---ar). + /// + public AspectRatio? AspectRatio + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("aspectRatio"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("aspectRatio", value); + } + } + + /// + /// Specifies the audio codec, e.g., `aac`, `opus`, or `none`. See [Audio codec](https://imagekit.io/docs/video-optimization#audio-codec---ac). + /// + public ApiEnum? AudioCodec + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("audioCodec"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("audioCodec", value); + } + } + + /// + /// Specifies the background to be used in conjunction with certain cropping strategies + /// when resizing an image. - A solid color: e.g., `red`, `F3F3F3`, `AAFF0010`. + /// See [Solid color background](https://imagekit.io/docs/effects-and-enhancements#solid-color-background). + /// - Dominant color: `dominant` extracts the dominant color from the image. See + /// [Dominant color background](https://imagekit.io/docs/effects-and-enhancements#dominant-color-background). + /// - Gradient: `gradient_dominant` or `gradient_dominant_2` creates a gradient + /// using the dominant colors. Optionally specify palette size (2 or 4), e.g., + /// `gradient_dominant_4`. See [Gradient background](https://imagekit.io/docs/effects-and-enhancements#gradient-background). + /// - A blurred background: e.g., `blurred`, `blurred_25_N15`, etc. See [Blurred + /// background](https://imagekit.io/docs/effects-and-enhancements#blurred-background). + /// - Expand the image boundaries using generative fill: `genfill`. Not supported + /// inside overlay. Optionally, control the background scene by passing a text + /// prompt: `genfill[:-prompt-${text}]` or `genfill[:-prompte-${urlencoded_base64_encoded_text}]`. + /// See [Generative fill background](https://imagekit.io/docs/ai-transformations#generative-fill-bg-genfill). + /// + public string? Background + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("background"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("background", value); + } + } + + /// + /// Specifies the Gaussian blur level. Accepts an integer value between 1 and + /// 100, or an expression like `bl-10`. See [Blur](https://imagekit.io/docs/effects-and-enhancements#blur---bl). + /// + public double? Blur + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("blur"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("blur", value); + } + } + + /// + /// Adds a border to the output media. Accepts a string in the format `<border-width>_<hex-code>` + /// (e.g., `5_FFF000` for a 5px yellow border), or an expression like `ih_div_20_FF00FF`. + /// See [Border](https://imagekit.io/docs/effects-and-enhancements#border---b). + /// + public string? Border + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("border"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("border", value); + } + } + + /// + /// Applies a color tint to the image. Accepts color and intensity as optional + /// parameters. - `co-color` - Color to apply (e.g., `red`, `blue`, `FF0022`). + /// Default is gray color. - `in-intensity` - Intensity of the color (0-100). + /// Default is 35. See [Colorize](https://imagekit.io/docs/effects-and-enhancements#colorize---e-colorize). + /// + public string? Colorize + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("colorize"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("colorize", value); + } + } + + /// + /// Indicates whether the output image should retain the original color profile. + /// See [Color profile](https://imagekit.io/docs/image-optimization#color-profile---cp). + /// + public bool? ColorProfile + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("colorProfile"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("colorProfile", value); + } + } + + /// + /// Replaces colors in the image. Supports three formats: - `toColor` - Replace + /// dominant color with the specified color. - `toColor_tolerance` - Replace + /// dominant color with specified tolerance (0-100). - `toColor_tolerance_fromColor` + /// - Replace a specific color with another within tolerance range. Colors can + /// be hex codes (e.g., `FF0022`) or names (e.g., `red`, `blue`). See [Color + /// replacement](https://imagekit.io/docs/effects-and-enhancements#color-replace---cr). + /// + public string? ColorReplace + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("colorReplace"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("colorReplace", value); + } + } + + /// + /// Automatically enhances the contrast of an image (contrast stretch). See [Contrast + /// Stretch](https://imagekit.io/docs/effects-and-enhancements#contrast-stretch---e-contrast). + /// + public ApiEnum? ContrastStretch + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "contrastStretch" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("contrastStretch", value); + } + } + + /// + /// Crop modes for image resizing. See [Crop modes & focus](https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus). + /// + public ApiEnum? Crop + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("crop"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("crop", value); + } + } + + /// + /// Additional crop modes for image resizing. See [Crop modes & focus](https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus). + /// + public ApiEnum? CropMode + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("cropMode"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("cropMode", value); + } + } + + /// + /// Specifies a fallback image if the resource is not found, e.g., a URL or file + /// path. See [Default image](https://imagekit.io/docs/image-transformation#default-image---di). + /// + public string? DefaultImage + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("defaultImage"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("defaultImage", value); + } + } + + /// + /// Distorts the shape of an image. Supports two modes: - Perspective distortion: + /// `p-x1_y1_x2_y2_x3_y3_x4_y4` changes the position of the four corners starting + /// clockwise from top-left. - Arc distortion: `a-degrees` curves the image upwards + /// (positive values) or downwards (negative values). See [Distort effect](https://imagekit.io/docs/effects-and-enhancements#distort---e-distort). + /// + public string? Distort + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("distort"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("distort", value); + } + } + + /// + /// Accepts values between 0.1 and 5, or `auto` for automatic device pixel ratio + /// (DPR) calculation. Also accepts arithmetic expressions. - Learn about [Arithmetic + /// expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). + /// - See [DPR](https://imagekit.io/docs/image-resize-and-crop#dpr---dpr). + /// + public double? Dpr + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("dpr"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("dpr", value); + } + } + + /// + /// Specifies the duration (in seconds) for trimming videos, e.g., `5` or `10.5`. + /// Typically used with startOffset to indicate the length from the start offset. + /// Arithmetic expressions are supported. See [Trim videos – Duration](https://imagekit.io/docs/trim-videos#duration---du). + /// + public TransformationDuration? Duration + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("duration"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("duration", value); + } + } + + /// + /// Specifies the end offset (in seconds) for trimming videos, e.g., `5` or `10.5`. + /// Typically used with startOffset to define a time window. Arithmetic expressions + /// are supported. See [Trim videos – End offset](https://imagekit.io/docs/trim-videos#end-offset---eo). + /// + public EndOffset? EndOffset + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("endOffset"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("endOffset", value); + } + } + + /// + /// Flips or mirrors an image either horizontally, vertically, or both. Acceptable + /// values: `h` (horizontal), `v` (vertical), `h_v` (horizontal and vertical), + /// or `v_h`. See [Flip](https://imagekit.io/docs/effects-and-enhancements#flip---fl). + /// + public ApiEnum? Flip + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("flip"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("flip", value); + } + } + + /// + /// Refines padding and cropping behavior for pad resize, maintain ratio, and + /// extract crop modes. Supports manual positions and coordinate-based focus. + /// With AI-based cropping, you can automatically keep key subjects in frame—such + /// as faces or detected objects (e.g., `fo-face`, `fo-person`, `fo-car`)— while + /// resizing. - See [Focus](https://imagekit.io/docs/image-resize-and-crop#focus---fo). + /// - [Object aware cropping](https://imagekit.io/docs/image-resize-and-crop#object-aware-cropping---fo-object-name) + /// + public string? Focus + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("focus"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("focus", value); + } + } + + /// + /// Specifies the output format for images or videos, e.g., `jpg`, `png`, `webp`, + /// `mp4`, or `auto`. You can also pass `orig` for images to return the original + /// format. ImageKit automatically delivers images and videos in the optimal + /// format based on device support unless overridden by the dashboard settings + /// or the format parameter. See [Image format](https://imagekit.io/docs/image-optimization#format---f) + /// and [Video format](https://imagekit.io/docs/video-optimization#format---f). + /// + public ApiEnum? Format + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("format"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("format", value); + } + } + + /// + /// Creates a linear gradient with two colors. Pass `true` for a default gradient, + /// or provide a string for a custom gradient. See [Gradient](https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient). + /// + public TransformationGradient? Gradient + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("gradient"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("gradient", value); + } + } + + /// + /// Enables a grayscale effect for images. See [Grayscale](https://imagekit.io/docs/effects-and-enhancements#grayscale---e-grayscale). + /// + public ApiEnum? Grayscale + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("grayscale"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("grayscale", value); + } + } + + /// + /// Specifies the height of the output. If a value between 0 and 1 is provided, + /// it is treated as a percentage (e.g., `0.5` represents 50% of the original + /// height). You can also supply arithmetic expressions (e.g., `ih_mul_0.5`). + /// Height transformation – [Images](https://imagekit.io/docs/image-resize-and-crop#height---h) + /// · [Videos](https://imagekit.io/docs/video-resize-and-crop#height---h) + /// + public TransformationHeight? Height + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("height"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("height", value); + } + } + + /// + /// Specifies whether the output image (in JPEG or PNG) should be compressed + /// losslessly. See [Lossless compression](https://imagekit.io/docs/image-optimization#lossless-webp-and-png---lo). + /// + public bool? Lossless + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("lossless"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("lossless", value); + } + } + + /// + /// By default, ImageKit removes all metadata during automatic image compression. + /// Set this to true to preserve metadata. See [Image metadata](https://imagekit.io/docs/image-optimization#image-metadata---md). + /// + public bool? Metadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("metadata"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("metadata", value); + } + } + + /// + /// Named transformation reference. See [Named transformations](https://imagekit.io/docs/transformations#named-transformations). + /// + public string? Named + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("named"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("named", value); + } + } + + /// + /// Specifies the opacity level of the output image. See [Opacity](https://imagekit.io/docs/effects-and-enhancements#opacity---o). + /// + public double? Opacity + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("opacity"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("opacity", value); + } + } + + /// + /// If set to true, serves the original file without applying any transformations. + /// See [Deliver original file as-is](https://imagekit.io/docs/core-delivery-features#deliver-original-file-as-is---orig-true). + /// + public bool? Original + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("original"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("original", value); + } + } + + /// + /// Specifies an overlay to be applied on the parent image or video. ImageKit + /// supports overlays including images, text, videos, subtitles, and solid colors. + /// See [Overlay using layers](https://imagekit.io/docs/transformations#overlay-using-layers). + /// + public Overlay? Overlay + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("overlay"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("overlay", value); + } + } + + /// + /// Extracts a specific page or frame from multi-page or layered files (PDF, + /// PSD, AI). For example, specify by number (e.g., `2`), a range (e.g., `3-4` + /// for the 2nd and 3rd layers), or by name (e.g., `name-layer-4` for a PSD layer). + /// See [Thumbnail extraction](https://imagekit.io/docs/vector-and-animated-images#get-thumbnail-from-psd-pdf-ai-eps-and-animated-files). + /// + public Page? Page + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("page"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("page", value); + } + } + + /// + /// Specifies whether the output JPEG image should be rendered progressively. + /// Progressive loading begins with a low-quality, pixelated version of the + /// full image, which gradually improves to provide a faster perceived load time. + /// See [Progressive images](https://imagekit.io/docs/image-optimization#progressive-image---pr). + /// + public bool? Progressive + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("progressive"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("progressive", value); + } + } + + /// + /// Specifies the quality of the output image for lossy formats such as JPEG, + /// WebP, and AVIF. A higher quality value results in a larger file size with + /// better quality, while a lower value produces a smaller file size with reduced + /// quality. See [Quality](https://imagekit.io/docs/image-optimization#quality---q). + /// + public double? Quality + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("quality"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("quality", value); + } + } + + /// + /// Specifies the corner radius for rounded corners. - Single value (positive + /// integer): Applied to all corners (e.g., `20`). - `max`: Creates a circular + /// or oval shape. - Per-corner array: Provide four underscore-separated values + /// representing top-left, top-right, bottom-right, and bottom-left corners respectively + /// (e.g., `10_20_30_40`). See [Radius](https://imagekit.io/docs/effects-and-enhancements#radius---r). + /// + public TransformationRadius? Radius + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("radius"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("radius", value); + } + } + + /// + /// Pass any transformation not directly supported by the SDK. This transformation + /// string is appended to the URL as provided. + /// + public string? Raw + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("raw"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("raw", value); + } + } + + /// + /// Specifies the rotation angle in degrees. Positive values rotate the image + /// clockwise; you can also use, for example, `N40` for counterclockwise rotation + /// or `auto` to use the orientation specified in the image's EXIF data. For + /// videos, only the following values are supported: 0, 90, 180, 270, or 360. + /// See [Rotate](https://imagekit.io/docs/effects-and-enhancements#rotate---rt). + /// + public TransformationRotation? Rotation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("rotation"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("rotation", value); + } + } + + /// + /// Adds a shadow beneath solid objects in an image with a transparent background. + /// For AI-based drop shadows, refer to aiDropShadow. Pass `true` for a default + /// shadow, or provide a string for a custom shadow. See [Shadow](https://imagekit.io/docs/effects-and-enhancements#shadow---e-shadow). + /// + public Shadow? Shadow + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("shadow"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("shadow", value); + } + } + + /// + /// Sharpens the input image, highlighting edges and finer details. Pass `true` + /// for default sharpening, or provide a numeric value for custom sharpening. + /// See [Sharpen](https://imagekit.io/docs/effects-and-enhancements#sharpen---e-sharpen). + /// + public Sharpen? Sharpen + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("sharpen"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("sharpen", value); + } + } + + /// + /// Specifies the start offset (in seconds) for trimming videos, e.g., `5` or + /// `10.5`. Arithmetic expressions are also supported. See [Trim videos – Start + /// offset](https://imagekit.io/docs/trim-videos#start-offset---so). + /// + public StartOffset? StartOffset + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("startOffset"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("startOffset", value); + } + } + + /// + /// An array of resolutions for adaptive bitrate streaming, e.g., [`240`, `360`, + /// `480`, `720`, `1080`]. See [Adaptive Bitrate Streaming](https://imagekit.io/docs/adaptive-bitrate-streaming). + /// + public IReadOnlyList>? StreamingResolutions + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct< + ImmutableArray> + >("streamingResolutions"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set>?>( + "streamingResolutions", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Useful for images with a solid or nearly solid background and a central object. + /// This parameter trims the background, leaving only the central object in the + /// output image. See [Trim edges](https://imagekit.io/docs/effects-and-enhancements#trim-edges---t). + /// + public Trim? Trim + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("trim"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("trim", value); + } + } + + /// + /// Applies Unsharp Masking (USM), an image sharpening technique. Pass `true` + /// for a default unsharp mask, or provide a string for a custom unsharp mask. + /// See [Unsharp Mask](https://imagekit.io/docs/effects-and-enhancements#unsharp-mask---e-usm). + /// + public UnsharpMask? UnsharpMask + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("unsharpMask"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("unsharpMask", value); + } + } + + /// + /// Specifies the video codec, e.g., `h264`, `vp9`, `av1`, or `none`. See [Video codec](https://imagekit.io/docs/video-optimization#video-codec---vc). + /// + public ApiEnum? VideoCodec + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("videoCodec"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("videoCodec", value); + } + } + + /// + /// Specifies the width of the output. If a value between 0 and 1 is provided, + /// it is treated as a percentage (e.g., `0.4` represents 40% of the original + /// width). You can also supply arithmetic expressions (e.g., `iw_div_2`). Width + /// transformation – [Images](https://imagekit.io/docs/image-resize-and-crop#width---w) + /// · [Videos](https://imagekit.io/docs/video-resize-and-crop#width---w) + /// + public TransformationWidth? Width + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("width"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("width", value); + } + } + + /// + /// Focus using cropped image coordinates - X coordinate. See [Focus using cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). + /// + public TransformationX? X + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("x"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("x", value); + } + } + + /// + /// Focus using cropped image coordinates - X center coordinate. See [Focus using + /// cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). + /// + public TransformationXCenter? XCenter + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("xCenter"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("xCenter", value); + } + } + + /// + /// Focus using cropped image coordinates - Y coordinate. See [Focus using cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). + /// + public TransformationY? Y + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("y"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("y", value); + } + } + + /// + /// Focus using cropped image coordinates - Y center coordinate. See [Focus using + /// cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). + /// + public TransformationYCenter? YCenter + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("yCenter"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("yCenter", value); + } + } + + /// + /// Accepts a numeric value that determines how much to zoom in or out of the + /// cropped area. It should be used in conjunction with fo-face or fo-<object_name>. + /// See [Zoom](https://imagekit.io/docs/image-resize-and-crop#zoom---z). + /// + public double? Zoom + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("zoom"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("zoom", value); + } + } + + /// + public override void Validate() + { + _ = this.AIChangeBackground; + this.AIDropShadow?.Validate(); + _ = this.AIEdit; + this.AIRemoveBackground?.Validate(); + this.AIRemoveBackgroundExternal?.Validate(); + this.AIRetouch?.Validate(); + this.AIUpscale?.Validate(); + this.AIVariation?.Validate(); + this.AspectRatio?.Validate(); + this.AudioCodec?.Validate(); + _ = this.Background; + _ = this.Blur; + _ = this.Border; + _ = this.Colorize; + _ = this.ColorProfile; + _ = this.ColorReplace; + this.ContrastStretch?.Validate(); + this.Crop?.Validate(); + this.CropMode?.Validate(); + _ = this.DefaultImage; + _ = this.Distort; + _ = this.Dpr; + this.Duration?.Validate(); + this.EndOffset?.Validate(); + this.Flip?.Validate(); + _ = this.Focus; + this.Format?.Validate(); + this.Gradient?.Validate(); + this.Grayscale?.Validate(); + this.Height?.Validate(); + _ = this.Lossless; + _ = this.Metadata; + _ = this.Named; + _ = this.Opacity; + _ = this.Original; + this.Overlay?.Validate(); + this.Page?.Validate(); + _ = this.Progressive; + _ = this.Quality; + this.Radius?.Validate(); + _ = this.Raw; + this.Rotation?.Validate(); + this.Shadow?.Validate(); + this.Sharpen?.Validate(); + this.StartOffset?.Validate(); + foreach (var item in this.StreamingResolutions ?? []) + { + item.Validate(); + } + this.Trim?.Validate(); + this.UnsharpMask?.Validate(); + this.VideoCodec?.Validate(); + this.Width?.Validate(); + this.X?.Validate(); + this.XCenter?.Validate(); + this.Y?.Validate(); + this.YCenter?.Validate(); + _ = this.Zoom; + } + + public Transformation() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Transformation(Transformation transformation) + : base(transformation) { } +#pragma warning restore CS8618 + + public Transformation(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Transformation(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Transformation FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class TransformationFromRaw : IFromRawJson +{ + /// + public Transformation FromRawUnchecked(IReadOnlyDictionary rawData) => + Transformation.FromRawUnchecked(rawData); +} + +/// +/// Adds an AI-based drop shadow around a foreground object on a transparent or removed +/// background. Optionally, control the direction, elevation, and saturation of the +/// light source (e.g., `az-45` to change light direction). Pass `true` for the default +/// drop shadow, or provide a string for a custom drop shadow. Supported inside overlay. +/// See [AI Drop Shadow](https://imagekit.io/docs/ai-transformations#ai-drop-shadow-e-dropshadow). +/// +[JsonConverter(typeof(AIDropShadowConverter))] +public record class AIDropShadow : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public AIDropShadow(AIDropShadowDefault value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public AIDropShadow(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public AIDropShadow(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDefault(out var value)) { + /// // `value` is of type `AIDropShadowDefault` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDefault([NotNullWhen(true)] out AIDropShadowDefault? value) + { + value = this.Value as AIDropShadowDefault; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (AIDropShadowDefault value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action default_, System::Action @string) + { + switch (this.Value) + { + case AIDropShadowDefault value: + default_(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of AIDropShadow" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (AIDropShadowDefault value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func default_, + System::Func @string + ) + { + return this.Value switch + { + AIDropShadowDefault value => default_(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of AIDropShadow" + ), + }; + } + + public static implicit operator AIDropShadow(AIDropShadowDefault value) => new(value); + + public static implicit operator AIDropShadow(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of AIDropShadow" + ); + } + this.Switch((default_) => default_.Validate(), (_) => { }); + } + + public virtual bool Equals(AIDropShadow? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + AIDropShadowDefault _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class AIDropShadowConverter : JsonConverter +{ + public override AIDropShadow? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + AIDropShadow value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(AIDropShadowDefaultConverter))] +public record class AIDropShadowDefault +{ + public JsonElement Element { get; private init; } + + public AIDropShadowDefault() + { + Element = JsonSerializer.SerializeToElement(true); + } + + internal AIDropShadowDefault(JsonElement element) + { + Element = element; + } + + /// + /// Validates that the instance's underlying value is the expected constant. + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public void Validate() + { + if (this != new AIDropShadowDefault()) + { + throw new ImageKitInvalidDataException("Invalid value given for 'AIDropShadowDefault'"); + } + } + + public override int GetHashCode() + { + return 0; + } + + public virtual bool Equals(AIDropShadowDefault? other) + { + if (other == null) + { + return false; + } + + return JsonElement.DeepEquals(this.Element, other.Element); + } +} + +class AIDropShadowDefaultConverter : JsonConverter +{ + public override AIDropShadowDefault? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return new(JsonSerializer.Deserialize(ref reader, options)); + } + + public override void Write( + Utf8JsonWriter writer, + AIDropShadowDefault value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Element, options); + } +} + +/// +/// Applies ImageKit's in-house background removal. Supported inside overlay. See +/// [AI Background Removal](https://imagekit.io/docs/ai-transformations#imagekit-background-removal-e-bgremove). +/// +[JsonConverter(typeof(AIRemoveBackgroundConverter))] +public enum AIRemoveBackground +{ + True, +} + +sealed class AIRemoveBackgroundConverter : JsonConverter +{ + public override AIRemoveBackground Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + true => AIRemoveBackground.True, + _ => (AIRemoveBackground)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + AIRemoveBackground value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + AIRemoveBackground.True => true, + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Uses third-party background removal. Note: It is recommended to use aiRemoveBackground, +/// ImageKit's in-house solution, which is more cost-effective. Supported inside +/// overlay. See [External Background Removal](https://imagekit.io/docs/ai-transformations#background-removal-e-removedotbg). +/// +[JsonConverter(typeof(AIRemoveBackgroundExternalConverter))] +public enum AIRemoveBackgroundExternal +{ + True, +} + +sealed class AIRemoveBackgroundExternalConverter : JsonConverter +{ + public override AIRemoveBackgroundExternal Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + true => AIRemoveBackgroundExternal.True, + _ => (AIRemoveBackgroundExternal)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + AIRemoveBackgroundExternal value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + AIRemoveBackgroundExternal.True => true, + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Performs AI-based retouching to improve faces or product shots. Not supported +/// inside overlay. See [AI Retouch](https://imagekit.io/docs/ai-transformations#retouch-e-retouch). +/// +[JsonConverter(typeof(AIRetouchConverter))] +public enum AIRetouch +{ + True, +} + +sealed class AIRetouchConverter : JsonConverter +{ + public override AIRetouch Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + true => AIRetouch.True, + _ => (AIRetouch)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + AIRetouch value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + AIRetouch.True => true, + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Upscales images beyond their original dimensions using AI. Not supported inside +/// overlay. See [AI Upscale](https://imagekit.io/docs/ai-transformations#upscale-e-upscale). +/// +[JsonConverter(typeof(AIUpscaleConverter))] +public enum AIUpscale +{ + True, +} + +sealed class AIUpscaleConverter : JsonConverter +{ + public override AIUpscale Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + true => AIUpscale.True, + _ => (AIUpscale)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + AIUpscale value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + AIUpscale.True => true, + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Generates a variation of an image using AI. This produces a new image with slight +/// variations from the original, such as changes in color, texture, and other visual +/// elements, while preserving the structure and essence of the original image. Not +/// supported inside overlay. See [AI Generate Variations](https://imagekit.io/docs/ai-transformations#generate-variations-of-an-image-e-genvar). +/// +[JsonConverter(typeof(AIVariationConverter))] +public enum AIVariation +{ + True, +} + +sealed class AIVariationConverter : JsonConverter +{ + public override AIVariation Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + true => AIVariation.True, + _ => (AIVariation)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + AIVariation value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + AIVariation.True => true, + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Specifies the aspect ratio for the output, e.g., "ar-4-3". Typically used with +/// either width or height (but not both). For example: aspectRatio = `4:3`, `4_3`, +/// or an expression like `iar_div_2`. See [Image resize and crop – Aspect ratio](https://imagekit.io/docs/image-resize-and-crop#aspect-ratio---ar). +/// +[JsonConverter(typeof(AspectRatioConverter))] +public record class AspectRatio : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public AspectRatio(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public AspectRatio(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public AspectRatio(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of AspectRatio" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of AspectRatio" + ), + }; + } + + public static implicit operator AspectRatio(double value) => new(value); + + public static implicit operator AspectRatio(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of AspectRatio"); + } + } + + public virtual bool Equals(AspectRatio? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class AspectRatioConverter : JsonConverter +{ + public override AspectRatio? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + AspectRatio value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Specifies the audio codec, e.g., `aac`, `opus`, or `none`. See [Audio codec](https://imagekit.io/docs/video-optimization#audio-codec---ac). +/// +[JsonConverter(typeof(AudioCodecConverter))] +public enum AudioCodec +{ + Aac, + Opus, + None, +} + +sealed class AudioCodecConverter : JsonConverter +{ + public override AudioCodec Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "aac" => AudioCodec.Aac, + "opus" => AudioCodec.Opus, + "none" => AudioCodec.None, + _ => (AudioCodec)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + AudioCodec value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + AudioCodec.Aac => "aac", + AudioCodec.Opus => "opus", + AudioCodec.None => "none", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Automatically enhances the contrast of an image (contrast stretch). See [Contrast +/// Stretch](https://imagekit.io/docs/effects-and-enhancements#contrast-stretch---e-contrast). +/// +[JsonConverter(typeof(ContrastStretchConverter))] +public enum ContrastStretch +{ + True, +} + +sealed class ContrastStretchConverter : JsonConverter +{ + public override ContrastStretch Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + true => ContrastStretch.True, + _ => (ContrastStretch)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + ContrastStretch value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + ContrastStretch.True => true, + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Crop modes for image resizing. See [Crop modes & focus](https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus). +/// +[JsonConverter(typeof(CropConverter))] +public enum Crop +{ + Force, + AtMax, + AtMaxEnlarge, + AtLeast, + MaintainRatio, + MaintainRatioNoEnlarge, +} + +sealed class CropConverter : JsonConverter +{ + public override Crop Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "force" => Crop.Force, + "at_max" => Crop.AtMax, + "at_max_enlarge" => Crop.AtMaxEnlarge, + "at_least" => Crop.AtLeast, + "maintain_ratio" => Crop.MaintainRatio, + "maintain_ratio_no_enlarge" => Crop.MaintainRatioNoEnlarge, + _ => (Crop)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, Crop value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + Crop.Force => "force", + Crop.AtMax => "at_max", + Crop.AtMaxEnlarge => "at_max_enlarge", + Crop.AtLeast => "at_least", + Crop.MaintainRatio => "maintain_ratio", + Crop.MaintainRatioNoEnlarge => "maintain_ratio_no_enlarge", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Additional crop modes for image resizing. See [Crop modes & focus](https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus). +/// +[JsonConverter(typeof(CropModeConverter))] +public enum CropMode +{ + PadResize, + Extract, + PadExtract, + PadResizeNoEnlarge, + PadExtractNoShrink, +} + +sealed class CropModeConverter : JsonConverter +{ + public override CropMode Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "pad_resize" => CropMode.PadResize, + "extract" => CropMode.Extract, + "pad_extract" => CropMode.PadExtract, + "pad_resize_no_enlarge" => CropMode.PadResizeNoEnlarge, + "pad_extract_no_shrink" => CropMode.PadExtractNoShrink, + _ => (CropMode)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, CropMode value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + CropMode.PadResize => "pad_resize", + CropMode.Extract => "extract", + CropMode.PadExtract => "pad_extract", + CropMode.PadResizeNoEnlarge => "pad_resize_no_enlarge", + CropMode.PadExtractNoShrink => "pad_extract_no_shrink", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Specifies the duration (in seconds) for trimming videos, e.g., `5` or `10.5`. +/// Typically used with startOffset to indicate the length from the start offset. +/// Arithmetic expressions are supported. See [Trim videos – Duration](https://imagekit.io/docs/trim-videos#duration---du). +/// +[JsonConverter(typeof(TransformationDurationConverter))] +public record class TransformationDuration : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public TransformationDuration(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TransformationDuration(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TransformationDuration(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationDuration" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationDuration" + ), + }; + } + + public static implicit operator TransformationDuration(double value) => new(value); + + public static implicit operator TransformationDuration(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationDuration" + ); + } + } + + public virtual bool Equals(TransformationDuration? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class TransformationDurationConverter : JsonConverter +{ + public override TransformationDuration? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + TransformationDuration value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Specifies the end offset (in seconds) for trimming videos, e.g., `5` or `10.5`. +/// Typically used with startOffset to define a time window. Arithmetic expressions +/// are supported. See [Trim videos – End offset](https://imagekit.io/docs/trim-videos#end-offset---eo). +/// +[JsonConverter(typeof(EndOffsetConverter))] +public record class EndOffset : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public EndOffset(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public EndOffset(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public EndOffset(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of EndOffset" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of EndOffset" + ), + }; + } + + public static implicit operator EndOffset(double value) => new(value); + + public static implicit operator EndOffset(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of EndOffset"); + } + } + + public virtual bool Equals(EndOffset? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class EndOffsetConverter : JsonConverter +{ + public override EndOffset? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + EndOffset value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Flips or mirrors an image either horizontally, vertically, or both. Acceptable +/// values: `h` (horizontal), `v` (vertical), `h_v` (horizontal and vertical), or +/// `v_h`. See [Flip](https://imagekit.io/docs/effects-and-enhancements#flip---fl). +/// +[JsonConverter(typeof(TransformationFlipConverter))] +public enum TransformationFlip +{ + H, + V, + HV, + VH, +} + +sealed class TransformationFlipConverter : JsonConverter +{ + public override TransformationFlip Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "h" => TransformationFlip.H, + "v" => TransformationFlip.V, + "h_v" => TransformationFlip.HV, + "v_h" => TransformationFlip.VH, + _ => (TransformationFlip)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + TransformationFlip value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + TransformationFlip.H => "h", + TransformationFlip.V => "v", + TransformationFlip.HV => "h_v", + TransformationFlip.VH => "v_h", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Specifies the output format for images or videos, e.g., `jpg`, `png`, `webp`, +/// `mp4`, or `auto`. You can also pass `orig` for images to return the original +/// format. ImageKit automatically delivers images and videos in the optimal format +/// based on device support unless overridden by the dashboard settings or the format +/// parameter. See [Image format](https://imagekit.io/docs/image-optimization#format---f) +/// and [Video format](https://imagekit.io/docs/video-optimization#format---f). +/// +[JsonConverter(typeof(FormatConverter))] +public enum Format +{ + Auto, + Webp, + Jpg, + Jpeg, + Png, + Gif, + Svg, + Mp4, + Webm, + Avif, + Orig, +} + +sealed class FormatConverter : JsonConverter +{ + public override Format Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "auto" => Format.Auto, + "webp" => Format.Webp, + "jpg" => Format.Jpg, + "jpeg" => Format.Jpeg, + "png" => Format.Png, + "gif" => Format.Gif, + "svg" => Format.Svg, + "mp4" => Format.Mp4, + "webm" => Format.Webm, + "avif" => Format.Avif, + "orig" => Format.Orig, + _ => (Format)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, Format value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + Format.Auto => "auto", + Format.Webp => "webp", + Format.Jpg => "jpg", + Format.Jpeg => "jpeg", + Format.Png => "png", + Format.Gif => "gif", + Format.Svg => "svg", + Format.Mp4 => "mp4", + Format.Webm => "webm", + Format.Avif => "avif", + Format.Orig => "orig", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Creates a linear gradient with two colors. Pass `true` for a default gradient, +/// or provide a string for a custom gradient. See [Gradient](https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient). +/// +[JsonConverter(typeof(TransformationGradientConverter))] +public record class TransformationGradient : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public TransformationGradient(TransformationGradientDefault value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TransformationGradient(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TransformationGradient(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDefault(out var value)) { + /// // `value` is of type `TransformationGradientDefault` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDefault([NotNullWhen(true)] out TransformationGradientDefault? value) + { + value = this.Value as TransformationGradientDefault; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (TransformationGradientDefault value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action default_, + System::Action @string + ) + { + switch (this.Value) + { + case TransformationGradientDefault value: + default_(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationGradient" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (TransformationGradientDefault value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func default_, + System::Func @string + ) + { + return this.Value switch + { + TransformationGradientDefault value => default_(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationGradient" + ), + }; + } + + public static implicit operator TransformationGradient(TransformationGradientDefault value) => + new(value); + + public static implicit operator TransformationGradient(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationGradient" + ); + } + this.Switch((default_) => default_.Validate(), (_) => { }); + } + + public virtual bool Equals(TransformationGradient? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + TransformationGradientDefault _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class TransformationGradientConverter : JsonConverter +{ + public override TransformationGradient? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + TransformationGradient value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(TransformationGradientDefaultConverter))] +public record class TransformationGradientDefault +{ + public JsonElement Element { get; private init; } + + public TransformationGradientDefault() + { + Element = JsonSerializer.SerializeToElement(true); + } + + internal TransformationGradientDefault(JsonElement element) + { + Element = element; + } + + /// + /// Validates that the instance's underlying value is the expected constant. + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public void Validate() + { + if (this != new TransformationGradientDefault()) + { + throw new ImageKitInvalidDataException( + "Invalid value given for 'TransformationGradientDefault'" + ); + } + } + + public override int GetHashCode() + { + return 0; + } + + public virtual bool Equals(TransformationGradientDefault? other) + { + if (other == null) + { + return false; + } + + return JsonElement.DeepEquals(this.Element, other.Element); + } +} + +class TransformationGradientDefaultConverter : JsonConverter +{ + public override TransformationGradientDefault? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return new(JsonSerializer.Deserialize(ref reader, options)); + } + + public override void Write( + Utf8JsonWriter writer, + TransformationGradientDefault value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Element, options); + } +} + +/// +/// Enables a grayscale effect for images. See [Grayscale](https://imagekit.io/docs/effects-and-enhancements#grayscale---e-grayscale). +/// +[JsonConverter(typeof(GrayscaleConverter))] +public enum Grayscale +{ + True, +} + +sealed class GrayscaleConverter : JsonConverter +{ + public override Grayscale Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + true => Grayscale.True, + _ => (Grayscale)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + Grayscale value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + Grayscale.True => true, + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Specifies the height of the output. If a value between 0 and 1 is provided, it +/// is treated as a percentage (e.g., `0.5` represents 50% of the original height). +/// You can also supply arithmetic expressions (e.g., `ih_mul_0.5`). Height transformation +/// – [Images](https://imagekit.io/docs/image-resize-and-crop#height---h) · [Videos](https://imagekit.io/docs/video-resize-and-crop#height---h) +/// +[JsonConverter(typeof(TransformationHeightConverter))] +public record class TransformationHeight : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public TransformationHeight(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TransformationHeight(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TransformationHeight(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationHeight" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationHeight" + ), + }; + } + + public static implicit operator TransformationHeight(double value) => new(value); + + public static implicit operator TransformationHeight(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationHeight" + ); + } + } + + public virtual bool Equals(TransformationHeight? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class TransformationHeightConverter : JsonConverter +{ + public override TransformationHeight? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + TransformationHeight value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Extracts a specific page or frame from multi-page or layered files (PDF, PSD, +/// AI). For example, specify by number (e.g., `2`), a range (e.g., `3-4` for the +/// 2nd and 3rd layers), or by name (e.g., `name-layer-4` for a PSD layer). See [Thumbnail +/// extraction](https://imagekit.io/docs/vector-and-animated-images#get-thumbnail-from-psd-pdf-ai-eps-and-animated-files). +/// +[JsonConverter(typeof(PageConverter))] +public record class Page : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public Page(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Page(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Page(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException("Data did not match any variant of Page"); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException("Data did not match any variant of Page"), + }; + } + + public static implicit operator Page(double value) => new(value); + + public static implicit operator Page(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of Page"); + } + } + + public virtual bool Equals(Page? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class PageConverter : JsonConverter +{ + public override Page? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, Page value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Specifies the corner radius for rounded corners. - Single value (positive integer): +/// Applied to all corners (e.g., `20`). - `max`: Creates a circular or oval shape. +/// - Per-corner array: Provide four underscore-separated values representing top-left, +/// top-right, bottom-right, and bottom-left corners respectively (e.g., `10_20_30_40`). +/// See [Radius](https://imagekit.io/docs/effects-and-enhancements#radius---r). +/// +[JsonConverter(typeof(TransformationRadiusConverter))] +public record class TransformationRadius : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public TransformationRadius(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TransformationRadius(TransformationRadiusMax value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TransformationRadius(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TransformationRadius(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickMax(out var value)) { + /// // `value` is of type `TransformationRadiusMax` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickMax([NotNullWhen(true)] out TransformationRadiusMax? value) + { + value = this.Value as TransformationRadiusMax; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (TransformationRadiusMax value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @double, + System::Action max, + System::Action @string + ) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case TransformationRadiusMax value: + max(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationRadius" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (TransformationRadiusMax value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @double, + System::Func max, + System::Func @string + ) + { + return this.Value switch + { + double value => @double(value), + TransformationRadiusMax value => max(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationRadius" + ), + }; + } + + public static implicit operator TransformationRadius(double value) => new(value); + + public static implicit operator TransformationRadius(TransformationRadiusMax value) => + new(value); + + public static implicit operator TransformationRadius(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationRadius" + ); + } + this.Switch((_) => { }, (max) => max.Validate(), (_) => { }); + } + + public virtual bool Equals(TransformationRadius? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + TransformationRadiusMax _ => 1, + string _ => 2, + _ => -1, + }; + } +} + +sealed class TransformationRadiusConverter : JsonConverter +{ + public override TransformationRadius? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + TransformationRadius value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(TransformationRadiusMaxConverter))] +public record class TransformationRadiusMax +{ + public JsonElement Element { get; private init; } + + public TransformationRadiusMax() + { + Element = JsonSerializer.SerializeToElement("max"); + } + + internal TransformationRadiusMax(JsonElement element) + { + Element = element; + } + + /// + /// Validates that the instance's underlying value is the expected constant. + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public void Validate() + { + if (this != new TransformationRadiusMax()) + { + throw new ImageKitInvalidDataException( + "Invalid value given for 'TransformationRadiusMax'" + ); + } + } + + public override int GetHashCode() + { + return 0; + } + + public virtual bool Equals(TransformationRadiusMax? other) + { + if (other == null) + { + return false; + } + + return JsonElement.DeepEquals(this.Element, other.Element); + } +} + +class TransformationRadiusMaxConverter : JsonConverter +{ + public override TransformationRadiusMax? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return new(JsonSerializer.Deserialize(ref reader, options)); + } + + public override void Write( + Utf8JsonWriter writer, + TransformationRadiusMax value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Element, options); + } +} + +/// +/// Specifies the rotation angle in degrees. Positive values rotate the image clockwise; +/// you can also use, for example, `N40` for counterclockwise rotation or `auto` +/// to use the orientation specified in the image's EXIF data. For videos, only the +/// following values are supported: 0, 90, 180, 270, or 360. See [Rotate](https://imagekit.io/docs/effects-and-enhancements#rotate---rt). +/// +[JsonConverter(typeof(TransformationRotationConverter))] +public record class TransformationRotation : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public TransformationRotation(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TransformationRotation(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TransformationRotation(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationRotation" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationRotation" + ), + }; + } + + public static implicit operator TransformationRotation(double value) => new(value); + + public static implicit operator TransformationRotation(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationRotation" + ); + } + } + + public virtual bool Equals(TransformationRotation? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class TransformationRotationConverter : JsonConverter +{ + public override TransformationRotation? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + TransformationRotation value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Adds a shadow beneath solid objects in an image with a transparent background. +/// For AI-based drop shadows, refer to aiDropShadow. Pass `true` for a default shadow, +/// or provide a string for a custom shadow. See [Shadow](https://imagekit.io/docs/effects-and-enhancements#shadow---e-shadow). +/// +[JsonConverter(typeof(ShadowConverter))] +public record class Shadow : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public Shadow(ShadowDefault value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Shadow(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Shadow(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDefault(out var value)) { + /// // `value` is of type `ShadowDefault` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDefault([NotNullWhen(true)] out ShadowDefault? value) + { + value = this.Value as ShadowDefault; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (ShadowDefault value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action default_, System::Action @string) + { + switch (this.Value) + { + case ShadowDefault value: + default_(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException("Data did not match any variant of Shadow"); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (ShadowDefault value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func default_, System::Func @string) + { + return this.Value switch + { + ShadowDefault value => default_(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException("Data did not match any variant of Shadow"), + }; + } + + public static implicit operator Shadow(ShadowDefault value) => new(value); + + public static implicit operator Shadow(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of Shadow"); + } + this.Switch((default_) => default_.Validate(), (_) => { }); + } + + public virtual bool Equals(Shadow? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + ShadowDefault _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class ShadowConverter : JsonConverter +{ + public override Shadow? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, Shadow value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(ShadowDefaultConverter))] +public record class ShadowDefault +{ + public JsonElement Element { get; private init; } + + public ShadowDefault() + { + Element = JsonSerializer.SerializeToElement(true); + } + + internal ShadowDefault(JsonElement element) + { + Element = element; + } + + /// + /// Validates that the instance's underlying value is the expected constant. + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public void Validate() + { + if (this != new ShadowDefault()) + { + throw new ImageKitInvalidDataException("Invalid value given for 'ShadowDefault'"); + } + } + + public override int GetHashCode() + { + return 0; + } + + public virtual bool Equals(ShadowDefault? other) + { + if (other == null) + { + return false; + } + + return JsonElement.DeepEquals(this.Element, other.Element); + } +} + +class ShadowDefaultConverter : JsonConverter +{ + public override ShadowDefault? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return new(JsonSerializer.Deserialize(ref reader, options)); + } + + public override void Write( + Utf8JsonWriter writer, + ShadowDefault value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Element, options); + } +} + +/// +/// Sharpens the input image, highlighting edges and finer details. Pass `true` for +/// default sharpening, or provide a numeric value for custom sharpening. See [Sharpen](https://imagekit.io/docs/effects-and-enhancements#sharpen---e-sharpen). +/// +[JsonConverter(typeof(SharpenConverter))] +public record class Sharpen : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public Sharpen(SharpenDefault value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Sharpen(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Sharpen(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDefault(out var value)) { + /// // `value` is of type `SharpenDefault` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDefault([NotNullWhen(true)] out SharpenDefault? value) + { + value = this.Value as SharpenDefault; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (SharpenDefault value) => {...}, + /// (double value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action default_, System::Action @double) + { + switch (this.Value) + { + case SharpenDefault value: + default_(value); + break; + case double value: + @double(value); + break; + default: + throw new ImageKitInvalidDataException("Data did not match any variant of Sharpen"); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (SharpenDefault value) => {...}, + /// (double value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func default_, System::Func @double) + { + return this.Value switch + { + SharpenDefault value => default_(value), + double value => @double(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of Sharpen" + ), + }; + } + + public static implicit operator Sharpen(SharpenDefault value) => new(value); + + public static implicit operator Sharpen(double value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of Sharpen"); + } + this.Switch((default_) => default_.Validate(), (_) => { }); + } + + public virtual bool Equals(Sharpen? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + SharpenDefault _ => 0, + double _ => 1, + _ => -1, + }; + } +} + +sealed class SharpenConverter : JsonConverter +{ + public override Sharpen? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, Sharpen value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(SharpenDefaultConverter))] +public record class SharpenDefault +{ + public JsonElement Element { get; private init; } + + public SharpenDefault() + { + Element = JsonSerializer.SerializeToElement(true); + } + + internal SharpenDefault(JsonElement element) + { + Element = element; + } + + /// + /// Validates that the instance's underlying value is the expected constant. + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public void Validate() + { + if (this != new SharpenDefault()) + { + throw new ImageKitInvalidDataException("Invalid value given for 'SharpenDefault'"); + } + } + + public override int GetHashCode() + { + return 0; + } + + public virtual bool Equals(SharpenDefault? other) + { + if (other == null) + { + return false; + } + + return JsonElement.DeepEquals(this.Element, other.Element); + } +} + +class SharpenDefaultConverter : JsonConverter +{ + public override SharpenDefault? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return new(JsonSerializer.Deserialize(ref reader, options)); + } + + public override void Write( + Utf8JsonWriter writer, + SharpenDefault value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Element, options); + } +} + +/// +/// Specifies the start offset (in seconds) for trimming videos, e.g., `5` or `10.5`. +/// Arithmetic expressions are also supported. See [Trim videos – Start offset](https://imagekit.io/docs/trim-videos#start-offset---so). +/// +[JsonConverter(typeof(StartOffsetConverter))] +public record class StartOffset : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public StartOffset(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public StartOffset(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public StartOffset(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of StartOffset" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of StartOffset" + ), + }; + } + + public static implicit operator StartOffset(double value) => new(value); + + public static implicit operator StartOffset(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of StartOffset"); + } + } + + public virtual bool Equals(StartOffset? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class StartOffsetConverter : JsonConverter +{ + public override StartOffset? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + StartOffset value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Useful for images with a solid or nearly solid background and a central object. +/// This parameter trims the background, leaving only the central object in the +/// output image. See [Trim edges](https://imagekit.io/docs/effects-and-enhancements#trim-edges---t). +/// +[JsonConverter(typeof(TrimConverter))] +public record class Trim : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public Trim(TrimDefault value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Trim(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Trim(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDefault(out var value)) { + /// // `value` is of type `TrimDefault` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDefault([NotNullWhen(true)] out TrimDefault? value) + { + value = this.Value as TrimDefault; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (TrimDefault value) => {...}, + /// (double value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action default_, System::Action @double) + { + switch (this.Value) + { + case TrimDefault value: + default_(value); + break; + case double value: + @double(value); + break; + default: + throw new ImageKitInvalidDataException("Data did not match any variant of Trim"); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (TrimDefault value) => {...}, + /// (double value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func default_, System::Func @double) + { + return this.Value switch + { + TrimDefault value => default_(value), + double value => @double(value), + _ => throw new ImageKitInvalidDataException("Data did not match any variant of Trim"), + }; + } + + public static implicit operator Trim(TrimDefault value) => new(value); + + public static implicit operator Trim(double value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of Trim"); + } + this.Switch((default_) => default_.Validate(), (_) => { }); + } + + public virtual bool Equals(Trim? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + TrimDefault _ => 0, + double _ => 1, + _ => -1, + }; + } +} + +sealed class TrimConverter : JsonConverter +{ + public override Trim? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, Trim value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(TrimDefaultConverter))] +public record class TrimDefault +{ + public JsonElement Element { get; private init; } + + public TrimDefault() + { + Element = JsonSerializer.SerializeToElement(true); + } + + internal TrimDefault(JsonElement element) + { + Element = element; + } + + /// + /// Validates that the instance's underlying value is the expected constant. + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public void Validate() + { + if (this != new TrimDefault()) + { + throw new ImageKitInvalidDataException("Invalid value given for 'TrimDefault'"); + } + } + + public override int GetHashCode() + { + return 0; + } + + public virtual bool Equals(TrimDefault? other) + { + if (other == null) + { + return false; + } + + return JsonElement.DeepEquals(this.Element, other.Element); + } +} + +class TrimDefaultConverter : JsonConverter +{ + public override TrimDefault? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return new(JsonSerializer.Deserialize(ref reader, options)); + } + + public override void Write( + Utf8JsonWriter writer, + TrimDefault value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Element, options); + } +} + +/// +/// Applies Unsharp Masking (USM), an image sharpening technique. Pass `true` for +/// a default unsharp mask, or provide a string for a custom unsharp mask. See [Unsharp +/// Mask](https://imagekit.io/docs/effects-and-enhancements#unsharp-mask---e-usm). +/// +[JsonConverter(typeof(UnsharpMaskConverter))] +public record class UnsharpMask : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public UnsharpMask(UnsharpMaskDefault value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UnsharpMask(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UnsharpMask(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDefault(out var value)) { + /// // `value` is of type `UnsharpMaskDefault` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDefault([NotNullWhen(true)] out UnsharpMaskDefault? value) + { + value = this.Value as UnsharpMaskDefault; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (UnsharpMaskDefault value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action default_, System::Action @string) + { + switch (this.Value) + { + case UnsharpMaskDefault value: + default_(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of UnsharpMask" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (UnsharpMaskDefault value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func default_, System::Func @string) + { + return this.Value switch + { + UnsharpMaskDefault value => default_(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of UnsharpMask" + ), + }; + } + + public static implicit operator UnsharpMask(UnsharpMaskDefault value) => new(value); + + public static implicit operator UnsharpMask(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException("Data did not match any variant of UnsharpMask"); + } + this.Switch((default_) => default_.Validate(), (_) => { }); + } + + public virtual bool Equals(UnsharpMask? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + UnsharpMaskDefault _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class UnsharpMaskConverter : JsonConverter +{ + public override UnsharpMask? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + UnsharpMask value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter(typeof(UnsharpMaskDefaultConverter))] +public record class UnsharpMaskDefault +{ + public JsonElement Element { get; private init; } + + public UnsharpMaskDefault() + { + Element = JsonSerializer.SerializeToElement(true); + } + + internal UnsharpMaskDefault(JsonElement element) + { + Element = element; + } + + /// + /// Validates that the instance's underlying value is the expected constant. + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public void Validate() + { + if (this != new UnsharpMaskDefault()) + { + throw new ImageKitInvalidDataException("Invalid value given for 'UnsharpMaskDefault'"); + } + } + + public override int GetHashCode() + { + return 0; + } + + public virtual bool Equals(UnsharpMaskDefault? other) + { + if (other == null) + { + return false; + } + + return JsonElement.DeepEquals(this.Element, other.Element); + } +} + +class UnsharpMaskDefaultConverter : JsonConverter +{ + public override UnsharpMaskDefault? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return new(JsonSerializer.Deserialize(ref reader, options)); + } + + public override void Write( + Utf8JsonWriter writer, + UnsharpMaskDefault value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Element, options); + } +} + +/// +/// Specifies the video codec, e.g., `h264`, `vp9`, `av1`, or `none`. See [Video codec](https://imagekit.io/docs/video-optimization#video-codec---vc). +/// +[JsonConverter(typeof(VideoCodecConverter))] +public enum VideoCodec +{ + H264, + Vp9, + Av1, + None, +} + +sealed class VideoCodecConverter : JsonConverter +{ + public override VideoCodec Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "h264" => VideoCodec.H264, + "vp9" => VideoCodec.Vp9, + "av1" => VideoCodec.Av1, + "none" => VideoCodec.None, + _ => (VideoCodec)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + VideoCodec value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + VideoCodec.H264 => "h264", + VideoCodec.Vp9 => "vp9", + VideoCodec.Av1 => "av1", + VideoCodec.None => "none", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Specifies the width of the output. If a value between 0 and 1 is provided, it +/// is treated as a percentage (e.g., `0.4` represents 40% of the original width). +/// You can also supply arithmetic expressions (e.g., `iw_div_2`). Width transformation +/// – [Images](https://imagekit.io/docs/image-resize-and-crop#width---w) · [Videos](https://imagekit.io/docs/video-resize-and-crop#width---w) +/// +[JsonConverter(typeof(TransformationWidthConverter))] +public record class TransformationWidth : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public TransformationWidth(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TransformationWidth(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TransformationWidth(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationWidth" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationWidth" + ), + }; + } + + public static implicit operator TransformationWidth(double value) => new(value); + + public static implicit operator TransformationWidth(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationWidth" + ); + } + } + + public virtual bool Equals(TransformationWidth? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class TransformationWidthConverter : JsonConverter +{ + public override TransformationWidth? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + TransformationWidth value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Focus using cropped image coordinates - X coordinate. See [Focus using cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). +/// +[JsonConverter(typeof(TransformationXConverter))] +public record class TransformationX : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public TransformationX(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TransformationX(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TransformationX(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationX" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationX" + ), + }; + } + + public static implicit operator TransformationX(double value) => new(value); + + public static implicit operator TransformationX(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationX" + ); + } + } + + public virtual bool Equals(TransformationX? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class TransformationXConverter : JsonConverter +{ + public override TransformationX? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + TransformationX value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Focus using cropped image coordinates - X center coordinate. See [Focus using +/// cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). +/// +[JsonConverter(typeof(TransformationXCenterConverter))] +public record class TransformationXCenter : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public TransformationXCenter(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TransformationXCenter(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TransformationXCenter(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationXCenter" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationXCenter" + ), + }; + } + + public static implicit operator TransformationXCenter(double value) => new(value); + + public static implicit operator TransformationXCenter(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationXCenter" + ); + } + } + + public virtual bool Equals(TransformationXCenter? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class TransformationXCenterConverter : JsonConverter +{ + public override TransformationXCenter? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + TransformationXCenter value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Focus using cropped image coordinates - Y coordinate. See [Focus using cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). +/// +[JsonConverter(typeof(TransformationYConverter))] +public record class TransformationY : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public TransformationY(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TransformationY(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TransformationY(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationY" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationY" + ), + }; + } + + public static implicit operator TransformationY(double value) => new(value); + + public static implicit operator TransformationY(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationY" + ); + } + } + + public virtual bool Equals(TransformationY? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class TransformationYConverter : JsonConverter +{ + public override TransformationY? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + TransformationY value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Focus using cropped image coordinates - Y center coordinate. See [Focus using +/// cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). +/// +[JsonConverter(typeof(TransformationYCenterConverter))] +public record class TransformationYCenter : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public TransformationYCenter(double value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TransformationYCenter(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public TransformationYCenter(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickDouble(out var value)) { + /// // `value` is of type `double` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickDouble([NotNullWhen(true)] out double? value) + { + value = this.Value as double?; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch(System::Action @double, System::Action @string) + { + switch (this.Value) + { + case double value: + @double(value); + break; + case string value: + @string(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationYCenter" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (double value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match(System::Func @double, System::Func @string) + { + return this.Value switch + { + double value => @double(value), + string value => @string(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationYCenter" + ), + }; + } + + public static implicit operator TransformationYCenter(double value) => new(value); + + public static implicit operator TransformationYCenter(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of TransformationYCenter" + ); + } + } + + public virtual bool Equals(TransformationYCenter? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + double _ => 0, + string _ => 1, + _ => -1, + }; + } +} + +sealed class TransformationYCenterConverter : JsonConverter +{ + public override TransformationYCenter? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + return new(JsonSerializer.Deserialize(element, options), element); + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + TransformationYCenter value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} diff --git a/src/Imagekit/Models/TransformationPosition.cs b/src/Imagekit/Models/TransformationPosition.cs new file mode 100644 index 00000000..eb8db8ea --- /dev/null +++ b/src/Imagekit/Models/TransformationPosition.cs @@ -0,0 +1,55 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models; + +/// +/// By default, the transformation string is added as a query parameter in the URL, +/// e.g., `?tr=w-100,h-100`. If you want to add the transformation string in the +/// path of the URL, set this to `path`. Learn more in the [Transformations guide](https://imagekit.io/docs/transformations). +/// +[JsonConverter(typeof(TransformationPositionConverter))] +public enum TransformationPosition +{ + Path, + Query, +} + +sealed class TransformationPositionConverter : JsonConverter +{ + public override TransformationPosition Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "path" => TransformationPosition.Path, + "query" => TransformationPosition.Query, + _ => (TransformationPosition)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + TransformationPosition value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + TransformationPosition.Path => "path", + TransformationPosition.Query => "query", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} diff --git a/src/Imagekit/Models/VersionInfo.cs b/src/Imagekit/Models/VersionInfo.cs new file mode 100644 index 00000000..57568974 --- /dev/null +++ b/src/Imagekit/Models/VersionInfo.cs @@ -0,0 +1,98 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models; + +/// +/// An object containing the file or file version's `id` (versionId) and `name`. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class VersionInfo : JsonModel +{ + /// + /// Unique identifier of the file version. + /// + public string? ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("id"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("id", value); + } + } + + /// + /// Name of the file version. + /// + public string? Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("name"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("name", value); + } + } + + /// + public override void Validate() + { + _ = this.ID; + _ = this.Name; + } + + public VersionInfo() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VersionInfo(VersionInfo versionInfo) + : base(versionInfo) { } +#pragma warning restore CS8618 + + public VersionInfo(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VersionInfo(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VersionInfo FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class VersionInfoFromRaw : IFromRawJson +{ + /// + public VersionInfo FromRawUnchecked(IReadOnlyDictionary rawData) => + VersionInfo.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/VideoOverlay.cs b/src/Imagekit/Models/VideoOverlay.cs new file mode 100644 index 00000000..272a7a2c --- /dev/null +++ b/src/Imagekit/Models/VideoOverlay.cs @@ -0,0 +1,446 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models; + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class VideoOverlay : JsonModel +{ + /// + /// Controls how the layer blends with the base image or underlying content. + /// Maps to `lm` in the URL. By default, layers completely cover the base image + /// beneath them. Layer modes change this behavior: - `multiply`: Multiplies + /// the pixel values of the layer with the base image. The result is always darker + /// than the original images. This is ideal for applying shadows or color tints. + /// - `displace`: Uses the layer as a displacement map to distort pixels in the + /// base image. The red channel controls horizontal displacement, and the green + /// channel controls vertical displacement. Requires `x` or `y` parameter to control + /// displacement magnitude. - `cutout`: Acts as an inverse mask where opaque areas + /// of the layer turn the base image transparent, while transparent areas leave + /// the base image unchanged. This mode functions like a hole-punch, effectively + /// cutting the shape of the layer out of the underlying image. - `cutter`: Acts + /// as a shape mask where only the parts of the base image that fall inside the + /// opaque area of the layer are preserved. This mode functions like a cookie-cutter, + /// trimming the base image to match the specific dimensions and shape of the + /// layer. See [Layer modes](https://imagekit.io/docs/add-overlays-on-images#layer-modes). + /// + public ApiEnum? LayerMode + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("layerMode"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("layerMode", value); + } + } + + public OverlayPosition? Position + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("position"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("position", value); + } + } + + public OverlayTiming? Timing + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("timing"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("timing", value); + } + } + + /// + /// Specifies the relative path to the video used as an overlay. + /// + public required string Input + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("input"); + } + init { this._rawData.Set("input", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// The input path can be included in the layer as either `i-{input}` or `ie-{base64_encoded_input}`. + /// By default, the SDK determines the appropriate format automatically. To + /// always use base64 encoding (`ie-{base64}`), set this parameter to `base64`. + /// To always use plain text (`i-{input}`), set it to `plain`. + /// + /// Regardless of the encoding method: - Leading and trailing slashes are + /// removed. - Remaining slashes within the path are replaced with `@@` when using + /// plain text. + /// + public ApiEnum? Encoding + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum + >("encoding"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("encoding", value); + } + } + + /// + /// Array of transformation to be applied to the overlay video. Except `streamingResolutions`, + /// all other video transformations are supported. See [Video transformations](https://imagekit.io/docs/video-transformation). + /// + public IReadOnlyList? Transformation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>( + "transformation" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "transformation", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + public static implicit operator BaseOverlay(VideoOverlay videoOverlay) => + new() + { + LayerMode = videoOverlay.LayerMode, + Position = videoOverlay.Position, + Timing = videoOverlay.Timing, + }; + + /// + public override void Validate() + { + this.LayerMode?.Validate(); + this.Position?.Validate(); + this.Timing?.Validate(); + _ = this.Input; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("video"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + this.Encoding?.Validate(); + foreach (var item in this.Transformation ?? []) + { + item.Validate(); + } + } + + public VideoOverlay() + { + this.Type = JsonSerializer.SerializeToElement("video"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoOverlay(VideoOverlay videoOverlay) + : base(videoOverlay) { } +#pragma warning restore CS8618 + + public VideoOverlay(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("video"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoOverlay(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoOverlay FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public VideoOverlay(string input) + : this() + { + this.Input = input; + } +} + +class VideoOverlayFromRaw : IFromRawJson +{ + /// + public VideoOverlay FromRawUnchecked(IReadOnlyDictionary rawData) => + VideoOverlay.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter) +)] +public sealed record class VideoOverlayVideoOverlay : JsonModel +{ + /// + /// Specifies the relative path to the video used as an overlay. + /// + public required string Input + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("input"); + } + init { this._rawData.Set("input", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// The input path can be included in the layer as either `i-{input}` or `ie-{base64_encoded_input}`. + /// By default, the SDK determines the appropriate format automatically. To + /// always use base64 encoding (`ie-{base64}`), set this parameter to `base64`. + /// To always use plain text (`i-{input}`), set it to `plain`. + /// + /// Regardless of the encoding method: - Leading and trailing slashes are + /// removed. - Remaining slashes within the path are replaced with `@@` when using + /// plain text. + /// + public ApiEnum? Encoding + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum + >("encoding"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("encoding", value); + } + } + + /// + /// Array of transformation to be applied to the overlay video. Except `streamingResolutions`, + /// all other video transformations are supported. See [Video transformations](https://imagekit.io/docs/video-transformation). + /// + public IReadOnlyList? Transformation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>( + "transformation" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "transformation", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.Input; + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("video"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + this.Encoding?.Validate(); + foreach (var item in this.Transformation ?? []) + { + item.Validate(); + } + } + + public VideoOverlayVideoOverlay() + { + this.Type = JsonSerializer.SerializeToElement("video"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoOverlayVideoOverlay(VideoOverlayVideoOverlay videoOverlayVideoOverlay) + : base(videoOverlayVideoOverlay) { } +#pragma warning restore CS8618 + + public VideoOverlayVideoOverlay(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("video"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoOverlayVideoOverlay(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoOverlayVideoOverlay FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public VideoOverlayVideoOverlay(string input) + : this() + { + this.Input = input; + } +} + +class VideoOverlayVideoOverlayFromRaw : IFromRawJson +{ + /// + public VideoOverlayVideoOverlay FromRawUnchecked( + IReadOnlyDictionary rawData + ) => VideoOverlayVideoOverlay.FromRawUnchecked(rawData); +} + +/// +/// The input path can be included in the layer as either `i-{input}` or `ie-{base64_encoded_input}`. +/// By default, the SDK determines the appropriate format automatically. To always +/// use base64 encoding (`ie-{base64}`), set this parameter to `base64`. To always +/// use plain text (`i-{input}`), set it to `plain`. +/// +/// Regardless of the encoding method: - Leading and trailing slashes are removed. +/// - Remaining slashes within the path are replaced with `@@` when using plain text. +/// +[JsonConverter(typeof(VideoOverlayVideoOverlayEncodingConverter))] +public enum VideoOverlayVideoOverlayEncoding +{ + Auto, + Plain, + Base64, +} + +sealed class VideoOverlayVideoOverlayEncodingConverter + : JsonConverter +{ + public override VideoOverlayVideoOverlayEncoding Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "auto" => VideoOverlayVideoOverlayEncoding.Auto, + "plain" => VideoOverlayVideoOverlayEncoding.Plain, + "base64" => VideoOverlayVideoOverlayEncoding.Base64, + _ => (VideoOverlayVideoOverlayEncoding)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + VideoOverlayVideoOverlayEncoding value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + VideoOverlayVideoOverlayEncoding.Auto => "auto", + VideoOverlayVideoOverlayEncoding.Plain => "plain", + VideoOverlayVideoOverlayEncoding.Base64 => "base64", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} diff --git a/src/Imagekit/Models/Webhooks/BaseWebhookEvent.cs b/src/Imagekit/Models/Webhooks/BaseWebhookEvent.cs new file mode 100644 index 00000000..10ca48d3 --- /dev/null +++ b/src/Imagekit/Models/Webhooks/BaseWebhookEvent.cs @@ -0,0 +1,81 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; + +namespace Imagekit.Models.Webhooks; + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class BaseWebhookEvent : JsonModel +{ + /// + /// Unique identifier for the event. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + /// + /// The type of webhook event. + /// + public required string Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + public override void Validate() + { + _ = this.ID; + _ = this.Type; + } + + public BaseWebhookEvent() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public BaseWebhookEvent(BaseWebhookEvent baseWebhookEvent) + : base(baseWebhookEvent) { } +#pragma warning restore CS8618 + + public BaseWebhookEvent(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + BaseWebhookEvent(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static BaseWebhookEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class BaseWebhookEventFromRaw : IFromRawJson +{ + /// + public BaseWebhookEvent FromRawUnchecked(IReadOnlyDictionary rawData) => + BaseWebhookEvent.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Webhooks/FileCreateEvent.cs b/src/Imagekit/Models/Webhooks/FileCreateEvent.cs new file mode 100644 index 00000000..4a3a2fbb --- /dev/null +++ b/src/Imagekit/Models/Webhooks/FileCreateEvent.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Files; + +namespace Imagekit.Models.Webhooks; + +/// +/// Triggered when a file is created. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class FileCreateEvent : JsonModel +{ + /// + /// Unique identifier for the event. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + /// + /// The type of webhook event. + /// + public required string Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Timestamp of when the event occurred in ISO8601 format. + /// + public required DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + /// + /// Object containing details of a file or file version. + /// + public required File Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("data"); + } + init { this._rawData.Set("data", value); } + } + + public static implicit operator BaseWebhookEvent(FileCreateEvent fileCreateEvent) => + new() { ID = fileCreateEvent.ID, Type = fileCreateEvent.Type }; + + /// + public override void Validate() + { + _ = this.ID; + _ = this.Type; + _ = this.CreatedAt; + this.Data.Validate(); + } + + public FileCreateEvent() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileCreateEvent(FileCreateEvent fileCreateEvent) + : base(fileCreateEvent) { } +#pragma warning restore CS8618 + + public FileCreateEvent(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileCreateEvent(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FileCreateEvent FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FileCreateEventFromRaw : IFromRawJson +{ + /// + public FileCreateEvent FromRawUnchecked(IReadOnlyDictionary rawData) => + FileCreateEvent.FromRawUnchecked(rawData); +} + +/// +/// Triggered when a file is created. +/// +[JsonConverter( + typeof(JsonModelConverter< + FileCreateEventFileCreateEvent, + FileCreateEventFileCreateEventFromRaw + >) +)] +public sealed record class FileCreateEventFileCreateEvent : JsonModel +{ + /// + /// Timestamp of when the event occurred in ISO8601 format. + /// + public required DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + /// + /// Object containing details of a file or file version. + /// + public required File Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("data"); + } + init { this._rawData.Set("data", value); } + } + + /// + /// Type of the webhook event. + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + public override void Validate() + { + _ = this.CreatedAt; + this.Data.Validate(); + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("file.created"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + } + + public FileCreateEventFileCreateEvent() + { + this.Type = JsonSerializer.SerializeToElement("file.created"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileCreateEventFileCreateEvent( + FileCreateEventFileCreateEvent fileCreateEventFileCreateEvent + ) + : base(fileCreateEventFileCreateEvent) { } +#pragma warning restore CS8618 + + public FileCreateEventFileCreateEvent(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("file.created"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileCreateEventFileCreateEvent(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FileCreateEventFileCreateEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FileCreateEventFileCreateEventFromRaw : IFromRawJson +{ + /// + public FileCreateEventFileCreateEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) => FileCreateEventFileCreateEvent.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Webhooks/FileDeleteEvent.cs b/src/Imagekit/Models/Webhooks/FileDeleteEvent.cs new file mode 100644 index 00000000..35e2d5f6 --- /dev/null +++ b/src/Imagekit/Models/Webhooks/FileDeleteEvent.cs @@ -0,0 +1,279 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; + +namespace Imagekit.Models.Webhooks; + +/// +/// Triggered when a file is deleted. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class FileDeleteEvent : JsonModel +{ + /// + /// Unique identifier for the event. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + /// + /// The type of webhook event. + /// + public required string Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Timestamp of when the event occurred in ISO8601 format. + /// + public required DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + public required Data Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("data"); + } + init { this._rawData.Set("data", value); } + } + + public static implicit operator BaseWebhookEvent(FileDeleteEvent fileDeleteEvent) => + new() { ID = fileDeleteEvent.ID, Type = fileDeleteEvent.Type }; + + /// + public override void Validate() + { + _ = this.ID; + _ = this.Type; + _ = this.CreatedAt; + this.Data.Validate(); + } + + public FileDeleteEvent() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileDeleteEvent(FileDeleteEvent fileDeleteEvent) + : base(fileDeleteEvent) { } +#pragma warning restore CS8618 + + public FileDeleteEvent(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileDeleteEvent(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FileDeleteEvent FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FileDeleteEventFromRaw : IFromRawJson +{ + /// + public FileDeleteEvent FromRawUnchecked(IReadOnlyDictionary rawData) => + FileDeleteEvent.FromRawUnchecked(rawData); +} + +/// +/// Triggered when a file is deleted. +/// +[JsonConverter( + typeof(JsonModelConverter< + FileDeleteEventFileDeleteEvent, + FileDeleteEventFileDeleteEventFromRaw + >) +)] +public sealed record class FileDeleteEventFileDeleteEvent : JsonModel +{ + /// + /// Timestamp of when the event occurred in ISO8601 format. + /// + public required DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + public required Data Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("data"); + } + init { this._rawData.Set("data", value); } + } + + /// + /// Type of the webhook event. + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + public override void Validate() + { + _ = this.CreatedAt; + this.Data.Validate(); + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("file.deleted"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + } + + public FileDeleteEventFileDeleteEvent() + { + this.Type = JsonSerializer.SerializeToElement("file.deleted"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileDeleteEventFileDeleteEvent( + FileDeleteEventFileDeleteEvent fileDeleteEventFileDeleteEvent + ) + : base(fileDeleteEventFileDeleteEvent) { } +#pragma warning restore CS8618 + + public FileDeleteEventFileDeleteEvent(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("file.deleted"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileDeleteEventFileDeleteEvent(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FileDeleteEventFileDeleteEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FileDeleteEventFileDeleteEventFromRaw : IFromRawJson +{ + /// + public FileDeleteEventFileDeleteEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) => FileDeleteEventFileDeleteEvent.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Data : JsonModel +{ + /// + /// The unique `fileId` of the deleted file. + /// + public required string FileID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("fileId"); + } + init { this._rawData.Set("fileId", value); } + } + + /// + public override void Validate() + { + _ = this.FileID; + } + + public Data() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Data(Data data) + : base(data) { } +#pragma warning restore CS8618 + + public Data(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Data(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Data FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public Data(string fileID) + : this() + { + this.FileID = fileID; + } +} + +class DataFromRaw : IFromRawJson +{ + /// + public Data FromRawUnchecked(IReadOnlyDictionary rawData) => + Data.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Webhooks/FileUpdateEvent.cs b/src/Imagekit/Models/Webhooks/FileUpdateEvent.cs new file mode 100644 index 00000000..0614fe9a --- /dev/null +++ b/src/Imagekit/Models/Webhooks/FileUpdateEvent.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Files; + +namespace Imagekit.Models.Webhooks; + +/// +/// Triggered when a file is updated. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class FileUpdateEvent : JsonModel +{ + /// + /// Unique identifier for the event. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + /// + /// The type of webhook event. + /// + public required string Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Timestamp of when the event occurred in ISO8601 format. + /// + public required DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + /// + /// Object containing details of a file or file version. + /// + public required File Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("data"); + } + init { this._rawData.Set("data", value); } + } + + public static implicit operator BaseWebhookEvent(FileUpdateEvent fileUpdateEvent) => + new() { ID = fileUpdateEvent.ID, Type = fileUpdateEvent.Type }; + + /// + public override void Validate() + { + _ = this.ID; + _ = this.Type; + _ = this.CreatedAt; + this.Data.Validate(); + } + + public FileUpdateEvent() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileUpdateEvent(FileUpdateEvent fileUpdateEvent) + : base(fileUpdateEvent) { } +#pragma warning restore CS8618 + + public FileUpdateEvent(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileUpdateEvent(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FileUpdateEvent FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FileUpdateEventFromRaw : IFromRawJson +{ + /// + public FileUpdateEvent FromRawUnchecked(IReadOnlyDictionary rawData) => + FileUpdateEvent.FromRawUnchecked(rawData); +} + +/// +/// Triggered when a file is updated. +/// +[JsonConverter( + typeof(JsonModelConverter< + FileUpdateEventFileUpdateEvent, + FileUpdateEventFileUpdateEventFromRaw + >) +)] +public sealed record class FileUpdateEventFileUpdateEvent : JsonModel +{ + /// + /// Timestamp of when the event occurred in ISO8601 format. + /// + public required DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + /// + /// Object containing details of a file or file version. + /// + public required File Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("data"); + } + init { this._rawData.Set("data", value); } + } + + /// + /// Type of the webhook event. + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + public override void Validate() + { + _ = this.CreatedAt; + this.Data.Validate(); + if (!JsonElement.DeepEquals(this.Type, JsonSerializer.SerializeToElement("file.updated"))) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + } + + public FileUpdateEventFileUpdateEvent() + { + this.Type = JsonSerializer.SerializeToElement("file.updated"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileUpdateEventFileUpdateEvent( + FileUpdateEventFileUpdateEvent fileUpdateEventFileUpdateEvent + ) + : base(fileUpdateEventFileUpdateEvent) { } +#pragma warning restore CS8618 + + public FileUpdateEventFileUpdateEvent(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("file.updated"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileUpdateEventFileUpdateEvent(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FileUpdateEventFileUpdateEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FileUpdateEventFileUpdateEventFromRaw : IFromRawJson +{ + /// + public FileUpdateEventFileUpdateEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) => FileUpdateEventFileUpdateEvent.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Webhooks/FileVersionCreateEvent.cs b/src/Imagekit/Models/Webhooks/FileVersionCreateEvent.cs new file mode 100644 index 00000000..e01435c7 --- /dev/null +++ b/src/Imagekit/Models/Webhooks/FileVersionCreateEvent.cs @@ -0,0 +1,234 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Files; + +namespace Imagekit.Models.Webhooks; + +/// +/// Triggered when a file version is created. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class FileVersionCreateEvent : JsonModel +{ + /// + /// Unique identifier for the event. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + /// + /// The type of webhook event. + /// + public required string Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Timestamp of when the event occurred in ISO8601 format. + /// + public required DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + /// + /// Object containing details of a file or file version. + /// + public required File Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("data"); + } + init { this._rawData.Set("data", value); } + } + + public static implicit operator BaseWebhookEvent( + FileVersionCreateEvent fileVersionCreateEvent + ) => new() { ID = fileVersionCreateEvent.ID, Type = fileVersionCreateEvent.Type }; + + /// + public override void Validate() + { + _ = this.ID; + _ = this.Type; + _ = this.CreatedAt; + this.Data.Validate(); + } + + public FileVersionCreateEvent() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileVersionCreateEvent(FileVersionCreateEvent fileVersionCreateEvent) + : base(fileVersionCreateEvent) { } +#pragma warning restore CS8618 + + public FileVersionCreateEvent(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileVersionCreateEvent(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FileVersionCreateEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FileVersionCreateEventFromRaw : IFromRawJson +{ + /// + public FileVersionCreateEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) => FileVersionCreateEvent.FromRawUnchecked(rawData); +} + +/// +/// Triggered when a file version is created. +/// +[JsonConverter( + typeof(JsonModelConverter< + FileVersionCreateEventFileVersionCreateEvent, + FileVersionCreateEventFileVersionCreateEventFromRaw + >) +)] +public sealed record class FileVersionCreateEventFileVersionCreateEvent : JsonModel +{ + /// + /// Timestamp of when the event occurred in ISO8601 format. + /// + public required DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + /// + /// Object containing details of a file or file version. + /// + public required File Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("data"); + } + init { this._rawData.Set("data", value); } + } + + /// + /// Type of the webhook event. + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + public override void Validate() + { + _ = this.CreatedAt; + this.Data.Validate(); + if ( + !JsonElement.DeepEquals( + this.Type, + JsonSerializer.SerializeToElement("file-version.created") + ) + ) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + } + + public FileVersionCreateEventFileVersionCreateEvent() + { + this.Type = JsonSerializer.SerializeToElement("file-version.created"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileVersionCreateEventFileVersionCreateEvent( + FileVersionCreateEventFileVersionCreateEvent fileVersionCreateEventFileVersionCreateEvent + ) + : base(fileVersionCreateEventFileVersionCreateEvent) { } +#pragma warning restore CS8618 + + public FileVersionCreateEventFileVersionCreateEvent( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("file-version.created"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileVersionCreateEventFileVersionCreateEvent(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FileVersionCreateEventFileVersionCreateEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FileVersionCreateEventFileVersionCreateEventFromRaw + : IFromRawJson +{ + /// + public FileVersionCreateEventFileVersionCreateEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) => FileVersionCreateEventFileVersionCreateEvent.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Webhooks/FileVersionDeleteEvent.cs b/src/Imagekit/Models/Webhooks/FileVersionDeleteEvent.cs new file mode 100644 index 00000000..ba2c5073 --- /dev/null +++ b/src/Imagekit/Models/Webhooks/FileVersionDeleteEvent.cs @@ -0,0 +1,315 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; + +namespace Imagekit.Models.Webhooks; + +/// +/// Triggered when a file version is deleted. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class FileVersionDeleteEvent : JsonModel +{ + /// + /// Unique identifier for the event. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + /// + /// The type of webhook event. + /// + public required string Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Timestamp of when the event occurred in ISO8601 format. + /// + public required DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + public required FileVersionDeleteEventFileVersionDeleteEventData Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "data" + ); + } + init { this._rawData.Set("data", value); } + } + + public static implicit operator BaseWebhookEvent( + FileVersionDeleteEvent fileVersionDeleteEvent + ) => new() { ID = fileVersionDeleteEvent.ID, Type = fileVersionDeleteEvent.Type }; + + /// + public override void Validate() + { + _ = this.ID; + _ = this.Type; + _ = this.CreatedAt; + this.Data.Validate(); + } + + public FileVersionDeleteEvent() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileVersionDeleteEvent(FileVersionDeleteEvent fileVersionDeleteEvent) + : base(fileVersionDeleteEvent) { } +#pragma warning restore CS8618 + + public FileVersionDeleteEvent(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileVersionDeleteEvent(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FileVersionDeleteEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FileVersionDeleteEventFromRaw : IFromRawJson +{ + /// + public FileVersionDeleteEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) => FileVersionDeleteEvent.FromRawUnchecked(rawData); +} + +/// +/// Triggered when a file version is deleted. +/// +[JsonConverter( + typeof(JsonModelConverter< + FileVersionDeleteEventFileVersionDeleteEvent, + FileVersionDeleteEventFileVersionDeleteEventFromRaw + >) +)] +public sealed record class FileVersionDeleteEventFileVersionDeleteEvent : JsonModel +{ + /// + /// Timestamp of when the event occurred in ISO8601 format. + /// + public required DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + public required FileVersionDeleteEventFileVersionDeleteEventData Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "data" + ); + } + init { this._rawData.Set("data", value); } + } + + /// + /// Type of the webhook event. + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + public override void Validate() + { + _ = this.CreatedAt; + this.Data.Validate(); + if ( + !JsonElement.DeepEquals( + this.Type, + JsonSerializer.SerializeToElement("file-version.deleted") + ) + ) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + } + + public FileVersionDeleteEventFileVersionDeleteEvent() + { + this.Type = JsonSerializer.SerializeToElement("file-version.deleted"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileVersionDeleteEventFileVersionDeleteEvent( + FileVersionDeleteEventFileVersionDeleteEvent fileVersionDeleteEventFileVersionDeleteEvent + ) + : base(fileVersionDeleteEventFileVersionDeleteEvent) { } +#pragma warning restore CS8618 + + public FileVersionDeleteEventFileVersionDeleteEvent( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("file-version.deleted"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileVersionDeleteEventFileVersionDeleteEvent(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FileVersionDeleteEventFileVersionDeleteEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FileVersionDeleteEventFileVersionDeleteEventFromRaw + : IFromRawJson +{ + /// + public FileVersionDeleteEventFileVersionDeleteEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) => FileVersionDeleteEventFileVersionDeleteEvent.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter< + FileVersionDeleteEventFileVersionDeleteEventData, + FileVersionDeleteEventFileVersionDeleteEventDataFromRaw + >) +)] +public sealed record class FileVersionDeleteEventFileVersionDeleteEventData : JsonModel +{ + /// + /// The unique `fileId` of the deleted file. + /// + public required string FileID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("fileId"); + } + init { this._rawData.Set("fileId", value); } + } + + /// + /// The unique `versionId` of the deleted file version. + /// + public required string VersionID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("versionId"); + } + init { this._rawData.Set("versionId", value); } + } + + /// + public override void Validate() + { + _ = this.FileID; + _ = this.VersionID; + } + + public FileVersionDeleteEventFileVersionDeleteEventData() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public FileVersionDeleteEventFileVersionDeleteEventData( + FileVersionDeleteEventFileVersionDeleteEventData fileVersionDeleteEventFileVersionDeleteEventData + ) + : base(fileVersionDeleteEventFileVersionDeleteEventData) { } +#pragma warning restore CS8618 + + public FileVersionDeleteEventFileVersionDeleteEventData( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + FileVersionDeleteEventFileVersionDeleteEventData(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static FileVersionDeleteEventFileVersionDeleteEventData FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class FileVersionDeleteEventFileVersionDeleteEventDataFromRaw + : IFromRawJson +{ + /// + public FileVersionDeleteEventFileVersionDeleteEventData FromRawUnchecked( + IReadOnlyDictionary rawData + ) => FileVersionDeleteEventFileVersionDeleteEventData.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Webhooks/UnsafeUnwrapWebhookEvent.cs b/src/Imagekit/Models/Webhooks/UnsafeUnwrapWebhookEvent.cs new file mode 100644 index 00000000..3f4c49b9 --- /dev/null +++ b/src/Imagekit/Models/Webhooks/UnsafeUnwrapWebhookEvent.cs @@ -0,0 +1,972 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models.Webhooks; + +/// +/// Triggered when a new video transformation request is accepted for processing. +/// This event confirms that ImageKit has received and queued your transformation +/// request. Use this for debugging and tracking transformation lifecycle. +/// +[JsonConverter(typeof(UnsafeUnwrapWebhookEventConverter))] +public record class UnsafeUnwrapWebhookEvent : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public string ID + { + get + { + return Match( + videoTransformationAccepted: (x) => x.ID, + videoTransformationReady: (x) => x.ID, + videoTransformationError: (x) => x.ID, + uploadPreTransformSuccess: (x) => x.ID, + uploadPreTransformError: (x) => x.ID, + uploadPostTransformSuccess: (x) => x.ID, + uploadPostTransformError: (x) => x.ID, + fileCreate: (x) => x.ID, + fileUpdate: (x) => x.ID, + fileDelete: (x) => x.ID, + fileVersionCreate: (x) => x.ID, + fileVersionDelete: (x) => x.ID + ); + } + } + + public string Type + { + get + { + return Match( + videoTransformationAccepted: (x) => x.Type, + videoTransformationReady: (x) => x.Type, + videoTransformationError: (x) => x.Type, + uploadPreTransformSuccess: (x) => x.Type, + uploadPreTransformError: (x) => x.Type, + uploadPostTransformSuccess: (x) => x.Type, + uploadPostTransformError: (x) => x.Type, + fileCreate: (x) => x.Type, + fileUpdate: (x) => x.Type, + fileDelete: (x) => x.Type, + fileVersionCreate: (x) => x.Type, + fileVersionDelete: (x) => x.Type + ); + } + } + + public System::DateTimeOffset CreatedAt + { + get + { + return Match( + videoTransformationAccepted: (x) => x.CreatedAt, + videoTransformationReady: (x) => x.CreatedAt, + videoTransformationError: (x) => x.CreatedAt, + uploadPreTransformSuccess: (x) => x.CreatedAt, + uploadPreTransformError: (x) => x.CreatedAt, + uploadPostTransformSuccess: (x) => x.CreatedAt, + uploadPostTransformError: (x) => x.CreatedAt, + fileCreate: (x) => x.CreatedAt, + fileUpdate: (x) => x.CreatedAt, + fileDelete: (x) => x.CreatedAt, + fileVersionCreate: (x) => x.CreatedAt, + fileVersionDelete: (x) => x.CreatedAt + ); + } + } + + public UnsafeUnwrapWebhookEvent( + VideoTransformationAcceptedEvent value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public UnsafeUnwrapWebhookEvent( + VideoTransformationReadyEvent value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public UnsafeUnwrapWebhookEvent( + VideoTransformationErrorEvent value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public UnsafeUnwrapWebhookEvent( + UploadPreTransformSuccessEvent value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public UnsafeUnwrapWebhookEvent(UploadPreTransformErrorEvent value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UnsafeUnwrapWebhookEvent( + UploadPostTransformSuccessEvent value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public UnsafeUnwrapWebhookEvent( + UploadPostTransformErrorEvent value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public UnsafeUnwrapWebhookEvent(FileCreateEvent value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UnsafeUnwrapWebhookEvent(FileUpdateEvent value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UnsafeUnwrapWebhookEvent(FileDeleteEvent value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UnsafeUnwrapWebhookEvent(FileVersionCreateEvent value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UnsafeUnwrapWebhookEvent(FileVersionDeleteEvent value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UnsafeUnwrapWebhookEvent(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickVideoTransformationAccepted(out var value)) { + /// // `value` is of type `VideoTransformationAcceptedEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickVideoTransformationAccepted( + [NotNullWhen(true)] out VideoTransformationAcceptedEvent? value + ) + { + value = this.Value as VideoTransformationAcceptedEvent; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickVideoTransformationReady(out var value)) { + /// // `value` is of type `VideoTransformationReadyEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickVideoTransformationReady( + [NotNullWhen(true)] out VideoTransformationReadyEvent? value + ) + { + value = this.Value as VideoTransformationReadyEvent; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickVideoTransformationError(out var value)) { + /// // `value` is of type `VideoTransformationErrorEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickVideoTransformationError( + [NotNullWhen(true)] out VideoTransformationErrorEvent? value + ) + { + value = this.Value as VideoTransformationErrorEvent; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickUploadPreTransformSuccess(out var value)) { + /// // `value` is of type `UploadPreTransformSuccessEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickUploadPreTransformSuccess( + [NotNullWhen(true)] out UploadPreTransformSuccessEvent? value + ) + { + value = this.Value as UploadPreTransformSuccessEvent; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickUploadPreTransformError(out var value)) { + /// // `value` is of type `UploadPreTransformErrorEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickUploadPreTransformError( + [NotNullWhen(true)] out UploadPreTransformErrorEvent? value + ) + { + value = this.Value as UploadPreTransformErrorEvent; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickUploadPostTransformSuccess(out var value)) { + /// // `value` is of type `UploadPostTransformSuccessEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickUploadPostTransformSuccess( + [NotNullWhen(true)] out UploadPostTransformSuccessEvent? value + ) + { + value = this.Value as UploadPostTransformSuccessEvent; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickUploadPostTransformError(out var value)) { + /// // `value` is of type `UploadPostTransformErrorEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickUploadPostTransformError( + [NotNullWhen(true)] out UploadPostTransformErrorEvent? value + ) + { + value = this.Value as UploadPostTransformErrorEvent; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickFileCreate(out var value)) { + /// // `value` is of type `FileCreateEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickFileCreate([NotNullWhen(true)] out FileCreateEvent? value) + { + value = this.Value as FileCreateEvent; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickFileUpdate(out var value)) { + /// // `value` is of type `FileUpdateEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickFileUpdate([NotNullWhen(true)] out FileUpdateEvent? value) + { + value = this.Value as FileUpdateEvent; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickFileDelete(out var value)) { + /// // `value` is of type `FileDeleteEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickFileDelete([NotNullWhen(true)] out FileDeleteEvent? value) + { + value = this.Value as FileDeleteEvent; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickFileVersionCreate(out var value)) { + /// // `value` is of type `FileVersionCreateEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickFileVersionCreate([NotNullWhen(true)] out FileVersionCreateEvent? value) + { + value = this.Value as FileVersionCreateEvent; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickFileVersionDelete(out var value)) { + /// // `value` is of type `FileVersionDeleteEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickFileVersionDelete([NotNullWhen(true)] out FileVersionDeleteEvent? value) + { + value = this.Value as FileVersionDeleteEvent; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (VideoTransformationAcceptedEvent value) => {...}, + /// (VideoTransformationReadyEvent value) => {...}, + /// (VideoTransformationErrorEvent value) => {...}, + /// (UploadPreTransformSuccessEvent value) => {...}, + /// (UploadPreTransformErrorEvent value) => {...}, + /// (UploadPostTransformSuccessEvent value) => {...}, + /// (UploadPostTransformErrorEvent value) => {...}, + /// (FileCreateEvent value) => {...}, + /// (FileUpdateEvent value) => {...}, + /// (FileDeleteEvent value) => {...}, + /// (FileVersionCreateEvent value) => {...}, + /// (FileVersionDeleteEvent value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action videoTransformationAccepted, + System::Action videoTransformationReady, + System::Action videoTransformationError, + System::Action uploadPreTransformSuccess, + System::Action uploadPreTransformError, + System::Action uploadPostTransformSuccess, + System::Action uploadPostTransformError, + System::Action fileCreate, + System::Action fileUpdate, + System::Action fileDelete, + System::Action fileVersionCreate, + System::Action fileVersionDelete + ) + { + switch (this.Value) + { + case VideoTransformationAcceptedEvent value: + videoTransformationAccepted(value); + break; + case VideoTransformationReadyEvent value: + videoTransformationReady(value); + break; + case VideoTransformationErrorEvent value: + videoTransformationError(value); + break; + case UploadPreTransformSuccessEvent value: + uploadPreTransformSuccess(value); + break; + case UploadPreTransformErrorEvent value: + uploadPreTransformError(value); + break; + case UploadPostTransformSuccessEvent value: + uploadPostTransformSuccess(value); + break; + case UploadPostTransformErrorEvent value: + uploadPostTransformError(value); + break; + case FileCreateEvent value: + fileCreate(value); + break; + case FileUpdateEvent value: + fileUpdate(value); + break; + case FileDeleteEvent value: + fileDelete(value); + break; + case FileVersionCreateEvent value: + fileVersionCreate(value); + break; + case FileVersionDeleteEvent value: + fileVersionDelete(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of UnsafeUnwrapWebhookEvent" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (VideoTransformationAcceptedEvent value) => {...}, + /// (VideoTransformationReadyEvent value) => {...}, + /// (VideoTransformationErrorEvent value) => {...}, + /// (UploadPreTransformSuccessEvent value) => {...}, + /// (UploadPreTransformErrorEvent value) => {...}, + /// (UploadPostTransformSuccessEvent value) => {...}, + /// (UploadPostTransformErrorEvent value) => {...}, + /// (FileCreateEvent value) => {...}, + /// (FileUpdateEvent value) => {...}, + /// (FileDeleteEvent value) => {...}, + /// (FileVersionCreateEvent value) => {...}, + /// (FileVersionDeleteEvent value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func videoTransformationAccepted, + System::Func videoTransformationReady, + System::Func videoTransformationError, + System::Func uploadPreTransformSuccess, + System::Func uploadPreTransformError, + System::Func uploadPostTransformSuccess, + System::Func uploadPostTransformError, + System::Func fileCreate, + System::Func fileUpdate, + System::Func fileDelete, + System::Func fileVersionCreate, + System::Func fileVersionDelete + ) + { + return this.Value switch + { + VideoTransformationAcceptedEvent value => videoTransformationAccepted(value), + VideoTransformationReadyEvent value => videoTransformationReady(value), + VideoTransformationErrorEvent value => videoTransformationError(value), + UploadPreTransformSuccessEvent value => uploadPreTransformSuccess(value), + UploadPreTransformErrorEvent value => uploadPreTransformError(value), + UploadPostTransformSuccessEvent value => uploadPostTransformSuccess(value), + UploadPostTransformErrorEvent value => uploadPostTransformError(value), + FileCreateEvent value => fileCreate(value), + FileUpdateEvent value => fileUpdate(value), + FileDeleteEvent value => fileDelete(value), + FileVersionCreateEvent value => fileVersionCreate(value), + FileVersionDeleteEvent value => fileVersionDelete(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of UnsafeUnwrapWebhookEvent" + ), + }; + } + + public static implicit operator UnsafeUnwrapWebhookEvent( + VideoTransformationAcceptedEvent value + ) => new(value); + + public static implicit operator UnsafeUnwrapWebhookEvent(VideoTransformationReadyEvent value) => + new(value); + + public static implicit operator UnsafeUnwrapWebhookEvent(VideoTransformationErrorEvent value) => + new(value); + + public static implicit operator UnsafeUnwrapWebhookEvent( + UploadPreTransformSuccessEvent value + ) => new(value); + + public static implicit operator UnsafeUnwrapWebhookEvent(UploadPreTransformErrorEvent value) => + new(value); + + public static implicit operator UnsafeUnwrapWebhookEvent( + UploadPostTransformSuccessEvent value + ) => new(value); + + public static implicit operator UnsafeUnwrapWebhookEvent(UploadPostTransformErrorEvent value) => + new(value); + + public static implicit operator UnsafeUnwrapWebhookEvent(FileCreateEvent value) => new(value); + + public static implicit operator UnsafeUnwrapWebhookEvent(FileUpdateEvent value) => new(value); + + public static implicit operator UnsafeUnwrapWebhookEvent(FileDeleteEvent value) => new(value); + + public static implicit operator UnsafeUnwrapWebhookEvent(FileVersionCreateEvent value) => + new(value); + + public static implicit operator UnsafeUnwrapWebhookEvent(FileVersionDeleteEvent value) => + new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of UnsafeUnwrapWebhookEvent" + ); + } + this.Switch( + (videoTransformationAccepted) => videoTransformationAccepted.Validate(), + (videoTransformationReady) => videoTransformationReady.Validate(), + (videoTransformationError) => videoTransformationError.Validate(), + (uploadPreTransformSuccess) => uploadPreTransformSuccess.Validate(), + (uploadPreTransformError) => uploadPreTransformError.Validate(), + (uploadPostTransformSuccess) => uploadPostTransformSuccess.Validate(), + (uploadPostTransformError) => uploadPostTransformError.Validate(), + (fileCreate) => fileCreate.Validate(), + (fileUpdate) => fileUpdate.Validate(), + (fileDelete) => fileDelete.Validate(), + (fileVersionCreate) => fileVersionCreate.Validate(), + (fileVersionDelete) => fileVersionDelete.Validate() + ); + } + + public virtual bool Equals(UnsafeUnwrapWebhookEvent? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + VideoTransformationAcceptedEvent _ => 0, + VideoTransformationReadyEvent _ => 1, + VideoTransformationErrorEvent _ => 2, + UploadPreTransformSuccessEvent _ => 3, + UploadPreTransformErrorEvent _ => 4, + UploadPostTransformSuccessEvent _ => 5, + UploadPostTransformErrorEvent _ => 6, + FileCreateEvent _ => 7, + FileUpdateEvent _ => 8, + FileDeleteEvent _ => 9, + FileVersionCreateEvent _ => 10, + FileVersionDeleteEvent _ => 11, + _ => -1, + }; + } +} + +sealed class UnsafeUnwrapWebhookEventConverter : JsonConverter +{ + public override UnsafeUnwrapWebhookEvent? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + string? type; + try + { + type = element.GetProperty("type").GetString(); + } + catch + { + type = null; + } + + switch (type) + { + default: + { + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + } + } + + public override void Write( + Utf8JsonWriter writer, + UnsafeUnwrapWebhookEvent value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} diff --git a/src/Imagekit/Models/Webhooks/UnwrapWebhookEvent.cs b/src/Imagekit/Models/Webhooks/UnwrapWebhookEvent.cs new file mode 100644 index 00000000..d39b6671 --- /dev/null +++ b/src/Imagekit/Models/Webhooks/UnwrapWebhookEvent.cs @@ -0,0 +1,949 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models.Webhooks; + +/// +/// Triggered when a new video transformation request is accepted for processing. +/// This event confirms that ImageKit has received and queued your transformation +/// request. Use this for debugging and tracking transformation lifecycle. +/// +[JsonConverter(typeof(UnwrapWebhookEventConverter))] +public record class UnwrapWebhookEvent : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public string ID + { + get + { + return Match( + videoTransformationAccepted: (x) => x.ID, + videoTransformationReady: (x) => x.ID, + videoTransformationError: (x) => x.ID, + uploadPreTransformSuccess: (x) => x.ID, + uploadPreTransformError: (x) => x.ID, + uploadPostTransformSuccess: (x) => x.ID, + uploadPostTransformError: (x) => x.ID, + fileCreate: (x) => x.ID, + fileUpdate: (x) => x.ID, + fileDelete: (x) => x.ID, + fileVersionCreate: (x) => x.ID, + fileVersionDelete: (x) => x.ID + ); + } + } + + public string Type + { + get + { + return Match( + videoTransformationAccepted: (x) => x.Type, + videoTransformationReady: (x) => x.Type, + videoTransformationError: (x) => x.Type, + uploadPreTransformSuccess: (x) => x.Type, + uploadPreTransformError: (x) => x.Type, + uploadPostTransformSuccess: (x) => x.Type, + uploadPostTransformError: (x) => x.Type, + fileCreate: (x) => x.Type, + fileUpdate: (x) => x.Type, + fileDelete: (x) => x.Type, + fileVersionCreate: (x) => x.Type, + fileVersionDelete: (x) => x.Type + ); + } + } + + public System::DateTimeOffset CreatedAt + { + get + { + return Match( + videoTransformationAccepted: (x) => x.CreatedAt, + videoTransformationReady: (x) => x.CreatedAt, + videoTransformationError: (x) => x.CreatedAt, + uploadPreTransformSuccess: (x) => x.CreatedAt, + uploadPreTransformError: (x) => x.CreatedAt, + uploadPostTransformSuccess: (x) => x.CreatedAt, + uploadPostTransformError: (x) => x.CreatedAt, + fileCreate: (x) => x.CreatedAt, + fileUpdate: (x) => x.CreatedAt, + fileDelete: (x) => x.CreatedAt, + fileVersionCreate: (x) => x.CreatedAt, + fileVersionDelete: (x) => x.CreatedAt + ); + } + } + + public UnwrapWebhookEvent(VideoTransformationAcceptedEvent value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UnwrapWebhookEvent(VideoTransformationReadyEvent value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UnwrapWebhookEvent(VideoTransformationErrorEvent value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UnwrapWebhookEvent(UploadPreTransformSuccessEvent value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UnwrapWebhookEvent(UploadPreTransformErrorEvent value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UnwrapWebhookEvent(UploadPostTransformSuccessEvent value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UnwrapWebhookEvent(UploadPostTransformErrorEvent value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UnwrapWebhookEvent(FileCreateEvent value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UnwrapWebhookEvent(FileUpdateEvent value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UnwrapWebhookEvent(FileDeleteEvent value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UnwrapWebhookEvent(FileVersionCreateEvent value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UnwrapWebhookEvent(FileVersionDeleteEvent value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public UnwrapWebhookEvent(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickVideoTransformationAccepted(out var value)) { + /// // `value` is of type `VideoTransformationAcceptedEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickVideoTransformationAccepted( + [NotNullWhen(true)] out VideoTransformationAcceptedEvent? value + ) + { + value = this.Value as VideoTransformationAcceptedEvent; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickVideoTransformationReady(out var value)) { + /// // `value` is of type `VideoTransformationReadyEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickVideoTransformationReady( + [NotNullWhen(true)] out VideoTransformationReadyEvent? value + ) + { + value = this.Value as VideoTransformationReadyEvent; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickVideoTransformationError(out var value)) { + /// // `value` is of type `VideoTransformationErrorEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickVideoTransformationError( + [NotNullWhen(true)] out VideoTransformationErrorEvent? value + ) + { + value = this.Value as VideoTransformationErrorEvent; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickUploadPreTransformSuccess(out var value)) { + /// // `value` is of type `UploadPreTransformSuccessEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickUploadPreTransformSuccess( + [NotNullWhen(true)] out UploadPreTransformSuccessEvent? value + ) + { + value = this.Value as UploadPreTransformSuccessEvent; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickUploadPreTransformError(out var value)) { + /// // `value` is of type `UploadPreTransformErrorEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickUploadPreTransformError( + [NotNullWhen(true)] out UploadPreTransformErrorEvent? value + ) + { + value = this.Value as UploadPreTransformErrorEvent; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickUploadPostTransformSuccess(out var value)) { + /// // `value` is of type `UploadPostTransformSuccessEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickUploadPostTransformSuccess( + [NotNullWhen(true)] out UploadPostTransformSuccessEvent? value + ) + { + value = this.Value as UploadPostTransformSuccessEvent; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickUploadPostTransformError(out var value)) { + /// // `value` is of type `UploadPostTransformErrorEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickUploadPostTransformError( + [NotNullWhen(true)] out UploadPostTransformErrorEvent? value + ) + { + value = this.Value as UploadPostTransformErrorEvent; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickFileCreate(out var value)) { + /// // `value` is of type `FileCreateEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickFileCreate([NotNullWhen(true)] out FileCreateEvent? value) + { + value = this.Value as FileCreateEvent; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickFileUpdate(out var value)) { + /// // `value` is of type `FileUpdateEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickFileUpdate([NotNullWhen(true)] out FileUpdateEvent? value) + { + value = this.Value as FileUpdateEvent; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickFileDelete(out var value)) { + /// // `value` is of type `FileDeleteEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickFileDelete([NotNullWhen(true)] out FileDeleteEvent? value) + { + value = this.Value as FileDeleteEvent; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickFileVersionCreate(out var value)) { + /// // `value` is of type `FileVersionCreateEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickFileVersionCreate([NotNullWhen(true)] out FileVersionCreateEvent? value) + { + value = this.Value as FileVersionCreateEvent; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickFileVersionDelete(out var value)) { + /// // `value` is of type `FileVersionDeleteEvent` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickFileVersionDelete([NotNullWhen(true)] out FileVersionDeleteEvent? value) + { + value = this.Value as FileVersionDeleteEvent; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (VideoTransformationAcceptedEvent value) => {...}, + /// (VideoTransformationReadyEvent value) => {...}, + /// (VideoTransformationErrorEvent value) => {...}, + /// (UploadPreTransformSuccessEvent value) => {...}, + /// (UploadPreTransformErrorEvent value) => {...}, + /// (UploadPostTransformSuccessEvent value) => {...}, + /// (UploadPostTransformErrorEvent value) => {...}, + /// (FileCreateEvent value) => {...}, + /// (FileUpdateEvent value) => {...}, + /// (FileDeleteEvent value) => {...}, + /// (FileVersionCreateEvent value) => {...}, + /// (FileVersionDeleteEvent value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action videoTransformationAccepted, + System::Action videoTransformationReady, + System::Action videoTransformationError, + System::Action uploadPreTransformSuccess, + System::Action uploadPreTransformError, + System::Action uploadPostTransformSuccess, + System::Action uploadPostTransformError, + System::Action fileCreate, + System::Action fileUpdate, + System::Action fileDelete, + System::Action fileVersionCreate, + System::Action fileVersionDelete + ) + { + switch (this.Value) + { + case VideoTransformationAcceptedEvent value: + videoTransformationAccepted(value); + break; + case VideoTransformationReadyEvent value: + videoTransformationReady(value); + break; + case VideoTransformationErrorEvent value: + videoTransformationError(value); + break; + case UploadPreTransformSuccessEvent value: + uploadPreTransformSuccess(value); + break; + case UploadPreTransformErrorEvent value: + uploadPreTransformError(value); + break; + case UploadPostTransformSuccessEvent value: + uploadPostTransformSuccess(value); + break; + case UploadPostTransformErrorEvent value: + uploadPostTransformError(value); + break; + case FileCreateEvent value: + fileCreate(value); + break; + case FileUpdateEvent value: + fileUpdate(value); + break; + case FileDeleteEvent value: + fileDelete(value); + break; + case FileVersionCreateEvent value: + fileVersionCreate(value); + break; + case FileVersionDeleteEvent value: + fileVersionDelete(value); + break; + default: + throw new ImageKitInvalidDataException( + "Data did not match any variant of UnwrapWebhookEvent" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (VideoTransformationAcceptedEvent value) => {...}, + /// (VideoTransformationReadyEvent value) => {...}, + /// (VideoTransformationErrorEvent value) => {...}, + /// (UploadPreTransformSuccessEvent value) => {...}, + /// (UploadPreTransformErrorEvent value) => {...}, + /// (UploadPostTransformSuccessEvent value) => {...}, + /// (UploadPostTransformErrorEvent value) => {...}, + /// (FileCreateEvent value) => {...}, + /// (FileUpdateEvent value) => {...}, + /// (FileDeleteEvent value) => {...}, + /// (FileVersionCreateEvent value) => {...}, + /// (FileVersionDeleteEvent value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func videoTransformationAccepted, + System::Func videoTransformationReady, + System::Func videoTransformationError, + System::Func uploadPreTransformSuccess, + System::Func uploadPreTransformError, + System::Func uploadPostTransformSuccess, + System::Func uploadPostTransformError, + System::Func fileCreate, + System::Func fileUpdate, + System::Func fileDelete, + System::Func fileVersionCreate, + System::Func fileVersionDelete + ) + { + return this.Value switch + { + VideoTransformationAcceptedEvent value => videoTransformationAccepted(value), + VideoTransformationReadyEvent value => videoTransformationReady(value), + VideoTransformationErrorEvent value => videoTransformationError(value), + UploadPreTransformSuccessEvent value => uploadPreTransformSuccess(value), + UploadPreTransformErrorEvent value => uploadPreTransformError(value), + UploadPostTransformSuccessEvent value => uploadPostTransformSuccess(value), + UploadPostTransformErrorEvent value => uploadPostTransformError(value), + FileCreateEvent value => fileCreate(value), + FileUpdateEvent value => fileUpdate(value), + FileDeleteEvent value => fileDelete(value), + FileVersionCreateEvent value => fileVersionCreate(value), + FileVersionDeleteEvent value => fileVersionDelete(value), + _ => throw new ImageKitInvalidDataException( + "Data did not match any variant of UnwrapWebhookEvent" + ), + }; + } + + public static implicit operator UnwrapWebhookEvent(VideoTransformationAcceptedEvent value) => + new(value); + + public static implicit operator UnwrapWebhookEvent(VideoTransformationReadyEvent value) => + new(value); + + public static implicit operator UnwrapWebhookEvent(VideoTransformationErrorEvent value) => + new(value); + + public static implicit operator UnwrapWebhookEvent(UploadPreTransformSuccessEvent value) => + new(value); + + public static implicit operator UnwrapWebhookEvent(UploadPreTransformErrorEvent value) => + new(value); + + public static implicit operator UnwrapWebhookEvent(UploadPostTransformSuccessEvent value) => + new(value); + + public static implicit operator UnwrapWebhookEvent(UploadPostTransformErrorEvent value) => + new(value); + + public static implicit operator UnwrapWebhookEvent(FileCreateEvent value) => new(value); + + public static implicit operator UnwrapWebhookEvent(FileUpdateEvent value) => new(value); + + public static implicit operator UnwrapWebhookEvent(FileDeleteEvent value) => new(value); + + public static implicit operator UnwrapWebhookEvent(FileVersionCreateEvent value) => new(value); + + public static implicit operator UnwrapWebhookEvent(FileVersionDeleteEvent value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new ImageKitInvalidDataException( + "Data did not match any variant of UnwrapWebhookEvent" + ); + } + this.Switch( + (videoTransformationAccepted) => videoTransformationAccepted.Validate(), + (videoTransformationReady) => videoTransformationReady.Validate(), + (videoTransformationError) => videoTransformationError.Validate(), + (uploadPreTransformSuccess) => uploadPreTransformSuccess.Validate(), + (uploadPreTransformError) => uploadPreTransformError.Validate(), + (uploadPostTransformSuccess) => uploadPostTransformSuccess.Validate(), + (uploadPostTransformError) => uploadPostTransformError.Validate(), + (fileCreate) => fileCreate.Validate(), + (fileUpdate) => fileUpdate.Validate(), + (fileDelete) => fileDelete.Validate(), + (fileVersionCreate) => fileVersionCreate.Validate(), + (fileVersionDelete) => fileVersionDelete.Validate() + ); + } + + public virtual bool Equals(UnwrapWebhookEvent? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + VideoTransformationAcceptedEvent _ => 0, + VideoTransformationReadyEvent _ => 1, + VideoTransformationErrorEvent _ => 2, + UploadPreTransformSuccessEvent _ => 3, + UploadPreTransformErrorEvent _ => 4, + UploadPostTransformSuccessEvent _ => 5, + UploadPostTransformErrorEvent _ => 6, + FileCreateEvent _ => 7, + FileUpdateEvent _ => 8, + FileDeleteEvent _ => 9, + FileVersionCreateEvent _ => 10, + FileVersionDeleteEvent _ => 11, + _ => -1, + }; + } +} + +sealed class UnwrapWebhookEventConverter : JsonConverter +{ + public override UnwrapWebhookEvent? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + string? type; + try + { + type = element.GetProperty("type").GetString(); + } + catch + { + type = null; + } + + switch (type) + { + default: + { + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) + when (e is JsonException || e is ImageKitInvalidDataException) + { + // ignore + } + + return new(element); + } + } + } + + public override void Write( + Utf8JsonWriter writer, + UnwrapWebhookEvent value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} diff --git a/src/Imagekit/Models/Webhooks/UploadPostTransformErrorEvent.cs b/src/Imagekit/Models/Webhooks/UploadPostTransformErrorEvent.cs new file mode 100644 index 00000000..eb772f73 --- /dev/null +++ b/src/Imagekit/Models/Webhooks/UploadPostTransformErrorEvent.cs @@ -0,0 +1,786 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models.Webhooks; + +/// +/// Triggered when a post-transformation fails. The original file remains available, +/// but the requested transformation could not be generated. +/// +[JsonConverter( + typeof(JsonModelConverter) +)] +public sealed record class UploadPostTransformErrorEvent : JsonModel +{ + /// + /// Unique identifier for the event. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + /// + /// The type of webhook event. + /// + public required string Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Timestamp of when the event occurred in ISO8601 format. + /// + public required System::DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + public required UploadPostTransformErrorEventUploadPostTransformErrorEventData Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "data" + ); + } + init { this._rawData.Set("data", value); } + } + + public required Request Request + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("request"); + } + init { this._rawData.Set("request", value); } + } + + public static implicit operator BaseWebhookEvent( + UploadPostTransformErrorEvent uploadPostTransformErrorEvent + ) => new() { ID = uploadPostTransformErrorEvent.ID, Type = uploadPostTransformErrorEvent.Type }; + + /// + public override void Validate() + { + _ = this.ID; + _ = this.Type; + _ = this.CreatedAt; + this.Data.Validate(); + this.Request.Validate(); + } + + public UploadPostTransformErrorEvent() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UploadPostTransformErrorEvent( + UploadPostTransformErrorEvent uploadPostTransformErrorEvent + ) + : base(uploadPostTransformErrorEvent) { } +#pragma warning restore CS8618 + + public UploadPostTransformErrorEvent(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UploadPostTransformErrorEvent(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UploadPostTransformErrorEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class UploadPostTransformErrorEventFromRaw : IFromRawJson +{ + /// + public UploadPostTransformErrorEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) => UploadPostTransformErrorEvent.FromRawUnchecked(rawData); +} + +/// +/// Triggered when a post-transformation fails. The original file remains available, +/// but the requested transformation could not be generated. +/// +[JsonConverter( + typeof(JsonModelConverter< + UploadPostTransformErrorEventUploadPostTransformErrorEvent, + UploadPostTransformErrorEventUploadPostTransformErrorEventFromRaw + >) +)] +public sealed record class UploadPostTransformErrorEventUploadPostTransformErrorEvent : JsonModel +{ + /// + /// Timestamp of when the event occurred in ISO8601 format. + /// + public required System::DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + public required UploadPostTransformErrorEventUploadPostTransformErrorEventData Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "data" + ); + } + init { this._rawData.Set("data", value); } + } + + public required Request Request + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("request"); + } + init { this._rawData.Set("request", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + public override void Validate() + { + _ = this.CreatedAt; + this.Data.Validate(); + this.Request.Validate(); + if ( + !JsonElement.DeepEquals( + this.Type, + JsonSerializer.SerializeToElement("upload.post-transform.error") + ) + ) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + } + + public UploadPostTransformErrorEventUploadPostTransformErrorEvent() + { + this.Type = JsonSerializer.SerializeToElement("upload.post-transform.error"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UploadPostTransformErrorEventUploadPostTransformErrorEvent( + UploadPostTransformErrorEventUploadPostTransformErrorEvent uploadPostTransformErrorEventUploadPostTransformErrorEvent + ) + : base(uploadPostTransformErrorEventUploadPostTransformErrorEvent) { } +#pragma warning restore CS8618 + + public UploadPostTransformErrorEventUploadPostTransformErrorEvent( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("upload.post-transform.error"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UploadPostTransformErrorEventUploadPostTransformErrorEvent( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UploadPostTransformErrorEventUploadPostTransformErrorEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class UploadPostTransformErrorEventUploadPostTransformErrorEventFromRaw + : IFromRawJson +{ + /// + public UploadPostTransformErrorEventUploadPostTransformErrorEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) => UploadPostTransformErrorEventUploadPostTransformErrorEvent.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter< + UploadPostTransformErrorEventUploadPostTransformErrorEventData, + UploadPostTransformErrorEventUploadPostTransformErrorEventDataFromRaw + >) +)] +public sealed record class UploadPostTransformErrorEventUploadPostTransformErrorEventData + : JsonModel +{ + /// + /// Unique identifier of the originally uploaded file. + /// + public required string FileID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("fileId"); + } + init { this._rawData.Set("fileId", value); } + } + + /// + /// Name of the file. + /// + public required string Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("name"); + } + init { this._rawData.Set("name", value); } + } + + /// + /// Path of the file. + /// + public required string Path + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("path"); + } + init { this._rawData.Set("path", value); } + } + + public required Transformation Transformation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("transformation"); + } + init { this._rawData.Set("transformation", value); } + } + + /// + /// URL of the attempted post-transformation. + /// + public required string Url + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("url"); + } + init { this._rawData.Set("url", value); } + } + + /// + public override void Validate() + { + _ = this.FileID; + _ = this.Name; + _ = this.Path; + this.Transformation.Validate(); + _ = this.Url; + } + + public UploadPostTransformErrorEventUploadPostTransformErrorEventData() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UploadPostTransformErrorEventUploadPostTransformErrorEventData( + UploadPostTransformErrorEventUploadPostTransformErrorEventData uploadPostTransformErrorEventUploadPostTransformErrorEventData + ) + : base(uploadPostTransformErrorEventUploadPostTransformErrorEventData) { } +#pragma warning restore CS8618 + + public UploadPostTransformErrorEventUploadPostTransformErrorEventData( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UploadPostTransformErrorEventUploadPostTransformErrorEventData( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UploadPostTransformErrorEventUploadPostTransformErrorEventData FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class UploadPostTransformErrorEventUploadPostTransformErrorEventDataFromRaw + : IFromRawJson +{ + /// + public UploadPostTransformErrorEventUploadPostTransformErrorEventData FromRawUnchecked( + IReadOnlyDictionary rawData + ) => UploadPostTransformErrorEventUploadPostTransformErrorEventData.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Transformation : JsonModel +{ + public required Error Error + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("error"); + } + init { this._rawData.Set("error", value); } + } + + /// + public override void Validate() + { + this.Error.Validate(); + } + + public Transformation() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Transformation(Transformation transformation) + : base(transformation) { } +#pragma warning restore CS8618 + + public Transformation(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Transformation(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Transformation FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public Transformation(Error error) + : this() + { + this.Error = error; + } +} + +class TransformationFromRaw : IFromRawJson +{ + /// + public Transformation FromRawUnchecked(IReadOnlyDictionary rawData) => + Transformation.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Error : JsonModel +{ + /// + /// Reason for the post-transformation failure. + /// + public required string Reason + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("reason"); + } + init { this._rawData.Set("reason", value); } + } + + /// + public override void Validate() + { + _ = this.Reason; + } + + public Error() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Error(Error error) + : base(error) { } +#pragma warning restore CS8618 + + public Error(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Error(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Error FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public Error(string reason) + : this() + { + this.Reason = reason; + } +} + +class ErrorFromRaw : IFromRawJson +{ + /// + public Error FromRawUnchecked(IReadOnlyDictionary rawData) => + Error.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Request : JsonModel +{ + public required RequestTransformation Transformation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("transformation"); + } + init { this._rawData.Set("transformation", value); } + } + + /// + /// Unique identifier for the originating request. + /// + public required string XRequestID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("x_request_id"); + } + init { this._rawData.Set("x_request_id", value); } + } + + /// + public override void Validate() + { + this.Transformation.Validate(); + _ = this.XRequestID; + } + + public Request() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Request(Request request) + : base(request) { } +#pragma warning restore CS8618 + + public Request(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Request(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Request FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class RequestFromRaw : IFromRawJson +{ + /// + public Request FromRawUnchecked(IReadOnlyDictionary rawData) => + Request.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class RequestTransformation : JsonModel +{ + /// + /// Type of the requested post-transformation. + /// + public required ApiEnum Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass< + ApiEnum + >("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Only applicable if transformation type is 'abs'. Streaming protocol used. + /// + public ApiEnum? Protocol + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("protocol"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("protocol", value); + } + } + + /// + /// Value for the requested transformation type. + /// + public string? Value + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("value"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("value", value); + } + } + + /// + public override void Validate() + { + this.Type.Validate(); + this.Protocol?.Validate(); + _ = this.Value; + } + + public RequestTransformation() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public RequestTransformation(RequestTransformation requestTransformation) + : base(requestTransformation) { } +#pragma warning restore CS8618 + + public RequestTransformation(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + RequestTransformation(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static RequestTransformation FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public RequestTransformation(ApiEnum type) + : this() + { + this.Type = type; + } +} + +class RequestTransformationFromRaw : IFromRawJson +{ + /// + public RequestTransformation FromRawUnchecked( + IReadOnlyDictionary rawData + ) => RequestTransformation.FromRawUnchecked(rawData); +} + +/// +/// Type of the requested post-transformation. +/// +[JsonConverter(typeof(TypeConverter))] +public enum Type +{ + Transformation, + Abs, + GifToVideo, + Thumbnail, +} + +sealed class TypeConverter : JsonConverter +{ + public override global::Imagekit.Models.Webhooks.Type Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "transformation" => global::Imagekit.Models.Webhooks.Type.Transformation, + "abs" => global::Imagekit.Models.Webhooks.Type.Abs, + "gif-to-video" => global::Imagekit.Models.Webhooks.Type.GifToVideo, + "thumbnail" => global::Imagekit.Models.Webhooks.Type.Thumbnail, + _ => (global::Imagekit.Models.Webhooks.Type)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + global::Imagekit.Models.Webhooks.Type value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + global::Imagekit.Models.Webhooks.Type.Transformation => "transformation", + global::Imagekit.Models.Webhooks.Type.Abs => "abs", + global::Imagekit.Models.Webhooks.Type.GifToVideo => "gif-to-video", + global::Imagekit.Models.Webhooks.Type.Thumbnail => "thumbnail", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Only applicable if transformation type is 'abs'. Streaming protocol used. +/// +[JsonConverter(typeof(ProtocolConverter))] +public enum Protocol +{ + Hls, + Dash, +} + +sealed class ProtocolConverter : JsonConverter +{ + public override Protocol Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "hls" => Protocol.Hls, + "dash" => Protocol.Dash, + _ => (Protocol)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, Protocol value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + Protocol.Hls => "hls", + Protocol.Dash => "dash", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} diff --git a/src/Imagekit/Models/Webhooks/UploadPostTransformSuccessEvent.cs b/src/Imagekit/Models/Webhooks/UploadPostTransformSuccessEvent.cs new file mode 100644 index 00000000..1671ec6f --- /dev/null +++ b/src/Imagekit/Models/Webhooks/UploadPostTransformSuccessEvent.cs @@ -0,0 +1,739 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models.Webhooks; + +/// +/// Triggered when a post-transformation completes successfully. The transformed +/// version of the file is now ready and can be accessed via the provided URL. Note +/// that each post-transformation generates a separate webhook event. +/// +[JsonConverter( + typeof(JsonModelConverter< + UploadPostTransformSuccessEvent, + UploadPostTransformSuccessEventFromRaw + >) +)] +public sealed record class UploadPostTransformSuccessEvent : JsonModel +{ + /// + /// Unique identifier for the event. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + /// + /// The type of webhook event. + /// + public required string Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Timestamp of when the event occurred in ISO8601 format. + /// + public required System::DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + public required UploadPostTransformSuccessEventUploadPostTransformSuccessEventData Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "data" + ); + } + init { this._rawData.Set("data", value); } + } + + public required UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest Request + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "request" + ); + } + init { this._rawData.Set("request", value); } + } + + public static implicit operator BaseWebhookEvent( + UploadPostTransformSuccessEvent uploadPostTransformSuccessEvent + ) => + new() + { + ID = uploadPostTransformSuccessEvent.ID, + Type = uploadPostTransformSuccessEvent.Type, + }; + + /// + public override void Validate() + { + _ = this.ID; + _ = this.Type; + _ = this.CreatedAt; + this.Data.Validate(); + this.Request.Validate(); + } + + public UploadPostTransformSuccessEvent() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UploadPostTransformSuccessEvent( + UploadPostTransformSuccessEvent uploadPostTransformSuccessEvent + ) + : base(uploadPostTransformSuccessEvent) { } +#pragma warning restore CS8618 + + public UploadPostTransformSuccessEvent(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UploadPostTransformSuccessEvent(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UploadPostTransformSuccessEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class UploadPostTransformSuccessEventFromRaw : IFromRawJson +{ + /// + public UploadPostTransformSuccessEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) => UploadPostTransformSuccessEvent.FromRawUnchecked(rawData); +} + +/// +/// Triggered when a post-transformation completes successfully. The transformed +/// version of the file is now ready and can be accessed via the provided URL. Note +/// that each post-transformation generates a separate webhook event. +/// +[JsonConverter( + typeof(JsonModelConverter< + UploadPostTransformSuccessEventUploadPostTransformSuccessEvent, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventFromRaw + >) +)] +public sealed record class UploadPostTransformSuccessEventUploadPostTransformSuccessEvent + : JsonModel +{ + /// + /// Timestamp of when the event occurred in ISO8601 format. + /// + public required System::DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + public required UploadPostTransformSuccessEventUploadPostTransformSuccessEventData Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "data" + ); + } + init { this._rawData.Set("data", value); } + } + + public required UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest Request + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "request" + ); + } + init { this._rawData.Set("request", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + public override void Validate() + { + _ = this.CreatedAt; + this.Data.Validate(); + this.Request.Validate(); + if ( + !JsonElement.DeepEquals( + this.Type, + JsonSerializer.SerializeToElement("upload.post-transform.success") + ) + ) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + } + + public UploadPostTransformSuccessEventUploadPostTransformSuccessEvent() + { + this.Type = JsonSerializer.SerializeToElement("upload.post-transform.success"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UploadPostTransformSuccessEventUploadPostTransformSuccessEvent( + UploadPostTransformSuccessEventUploadPostTransformSuccessEvent uploadPostTransformSuccessEventUploadPostTransformSuccessEvent + ) + : base(uploadPostTransformSuccessEventUploadPostTransformSuccessEvent) { } +#pragma warning restore CS8618 + + public UploadPostTransformSuccessEventUploadPostTransformSuccessEvent( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("upload.post-transform.success"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UploadPostTransformSuccessEventUploadPostTransformSuccessEvent( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UploadPostTransformSuccessEventUploadPostTransformSuccessEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class UploadPostTransformSuccessEventUploadPostTransformSuccessEventFromRaw + : IFromRawJson +{ + /// + public UploadPostTransformSuccessEventUploadPostTransformSuccessEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) => UploadPostTransformSuccessEventUploadPostTransformSuccessEvent.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter< + UploadPostTransformSuccessEventUploadPostTransformSuccessEventData, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventDataFromRaw + >) +)] +public sealed record class UploadPostTransformSuccessEventUploadPostTransformSuccessEventData + : JsonModel +{ + /// + /// Unique identifier of the originally uploaded file. + /// + public required string FileID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("fileId"); + } + init { this._rawData.Set("fileId", value); } + } + + /// + /// Name of the file. + /// + public required string Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("name"); + } + init { this._rawData.Set("name", value); } + } + + /// + /// URL of the generated post-transformation. + /// + public required string Url + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("url"); + } + init { this._rawData.Set("url", value); } + } + + /// + public override void Validate() + { + _ = this.FileID; + _ = this.Name; + _ = this.Url; + } + + public UploadPostTransformSuccessEventUploadPostTransformSuccessEventData() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UploadPostTransformSuccessEventUploadPostTransformSuccessEventData( + UploadPostTransformSuccessEventUploadPostTransformSuccessEventData uploadPostTransformSuccessEventUploadPostTransformSuccessEventData + ) + : base(uploadPostTransformSuccessEventUploadPostTransformSuccessEventData) { } +#pragma warning restore CS8618 + + public UploadPostTransformSuccessEventUploadPostTransformSuccessEventData( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UploadPostTransformSuccessEventUploadPostTransformSuccessEventData( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UploadPostTransformSuccessEventUploadPostTransformSuccessEventData FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class UploadPostTransformSuccessEventUploadPostTransformSuccessEventDataFromRaw + : IFromRawJson +{ + /// + public UploadPostTransformSuccessEventUploadPostTransformSuccessEventData FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + UploadPostTransformSuccessEventUploadPostTransformSuccessEventData.FromRawUnchecked( + rawData + ); +} + +[JsonConverter( + typeof(JsonModelConverter< + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestFromRaw + >) +)] +public sealed record class UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest + : JsonModel +{ + public required UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation Transformation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "transformation" + ); + } + init { this._rawData.Set("transformation", value); } + } + + /// + /// Unique identifier for the originating request. + /// + public required string XRequestID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("x_request_id"); + } + init { this._rawData.Set("x_request_id", value); } + } + + /// + public override void Validate() + { + this.Transformation.Validate(); + _ = this.XRequestID; + } + + public UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest( + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest uploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest + ) + : base(uploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest) { } +#pragma warning restore CS8618 + + public UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestFromRaw + : IFromRawJson +{ + /// + public UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequest.FromRawUnchecked( + rawData + ); +} + +[JsonConverter( + typeof(JsonModelConverter< + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationFromRaw + >) +)] +public sealed record class UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation + : JsonModel +{ + /// + /// Type of the requested post-transformation. + /// + public required ApiEnum< + string, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType + > Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass< + ApiEnum< + string, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType + > + >("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Only applicable if transformation type is 'abs'. Streaming protocol used. + /// + public ApiEnum< + string, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol + >? Protocol + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum< + string, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol + > + >("protocol"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("protocol", value); + } + } + + /// + /// Value for the requested transformation type. + /// + public string? Value + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("value"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("value", value); + } + } + + /// + public override void Validate() + { + this.Type.Validate(); + this.Protocol?.Validate(); + _ = this.Value; + } + + public UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation( + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation uploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation + ) + : base(uploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation) + { } +#pragma warning restore CS8618 + + public UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation( + ApiEnum< + string, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType + > type + ) + : this() + { + this.Type = type; + } +} + +class UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationFromRaw + : IFromRawJson +{ + /// + public UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformation.FromRawUnchecked( + rawData + ); +} + +/// +/// Type of the requested post-transformation. +/// +[JsonConverter( + typeof(UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationTypeConverter) +)] +public enum UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType +{ + Transformation, + Abs, + GifToVideo, + Thumbnail, +} + +sealed class UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationTypeConverter + : JsonConverter +{ + public override UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "transformation" => + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation, + "abs" => + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Abs, + "gif-to-video" => + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.GifToVideo, + "thumbnail" => + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Thumbnail, + _ => + (UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType)( + -1 + ), + }; + } + + public override void Write( + Utf8JsonWriter writer, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Transformation => + "transformation", + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Abs => + "abs", + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.GifToVideo => + "gif-to-video", + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationType.Thumbnail => + "thumbnail", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Only applicable if transformation type is 'abs'. Streaming protocol used. +/// +[JsonConverter( + typeof(UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocolConverter) +)] +public enum UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol +{ + Hls, + Dash, +} + +sealed class UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocolConverter + : JsonConverter +{ + public override UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "hls" => + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls, + "dash" => + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Dash, + _ => + (UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol)( + -1 + ), + }; + } + + public override void Write( + Utf8JsonWriter writer, + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Hls => + "hls", + UploadPostTransformSuccessEventUploadPostTransformSuccessEventRequestTransformationProtocol.Dash => + "dash", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} diff --git a/src/Imagekit/Models/Webhooks/UploadPreTransformErrorEvent.cs b/src/Imagekit/Models/Webhooks/UploadPreTransformErrorEvent.cs new file mode 100644 index 00000000..07bb775e --- /dev/null +++ b/src/Imagekit/Models/Webhooks/UploadPreTransformErrorEvent.cs @@ -0,0 +1,615 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; + +namespace Imagekit.Models.Webhooks; + +/// +/// Triggered when a pre-transformation fails. The file upload may have been accepted, +/// but the requested transformation could not be applied. +/// +[JsonConverter( + typeof(JsonModelConverter) +)] +public sealed record class UploadPreTransformErrorEvent : JsonModel +{ + /// + /// Unique identifier for the event. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + /// + /// The type of webhook event. + /// + public required string Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Timestamp of when the event occurred in ISO8601 format. + /// + public required DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + public required UploadPreTransformErrorEventUploadPreTransformErrorEventData Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "data" + ); + } + init { this._rawData.Set("data", value); } + } + + public required UploadPreTransformErrorEventUploadPreTransformErrorEventRequest Request + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "request" + ); + } + init { this._rawData.Set("request", value); } + } + + public static implicit operator BaseWebhookEvent( + UploadPreTransformErrorEvent uploadPreTransformErrorEvent + ) => new() { ID = uploadPreTransformErrorEvent.ID, Type = uploadPreTransformErrorEvent.Type }; + + /// + public override void Validate() + { + _ = this.ID; + _ = this.Type; + _ = this.CreatedAt; + this.Data.Validate(); + this.Request.Validate(); + } + + public UploadPreTransformErrorEvent() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UploadPreTransformErrorEvent(UploadPreTransformErrorEvent uploadPreTransformErrorEvent) + : base(uploadPreTransformErrorEvent) { } +#pragma warning restore CS8618 + + public UploadPreTransformErrorEvent(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UploadPreTransformErrorEvent(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UploadPreTransformErrorEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class UploadPreTransformErrorEventFromRaw : IFromRawJson +{ + /// + public UploadPreTransformErrorEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) => UploadPreTransformErrorEvent.FromRawUnchecked(rawData); +} + +/// +/// Triggered when a pre-transformation fails. The file upload may have been accepted, +/// but the requested transformation could not be applied. +/// +[JsonConverter( + typeof(JsonModelConverter< + UploadPreTransformErrorEventUploadPreTransformErrorEvent, + UploadPreTransformErrorEventUploadPreTransformErrorEventFromRaw + >) +)] +public sealed record class UploadPreTransformErrorEventUploadPreTransformErrorEvent : JsonModel +{ + /// + /// Timestamp of when the event occurred in ISO8601 format. + /// + public required DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + public required UploadPreTransformErrorEventUploadPreTransformErrorEventData Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "data" + ); + } + init { this._rawData.Set("data", value); } + } + + public required UploadPreTransformErrorEventUploadPreTransformErrorEventRequest Request + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "request" + ); + } + init { this._rawData.Set("request", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + public override void Validate() + { + _ = this.CreatedAt; + this.Data.Validate(); + this.Request.Validate(); + if ( + !JsonElement.DeepEquals( + this.Type, + JsonSerializer.SerializeToElement("upload.pre-transform.error") + ) + ) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + } + + public UploadPreTransformErrorEventUploadPreTransformErrorEvent() + { + this.Type = JsonSerializer.SerializeToElement("upload.pre-transform.error"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UploadPreTransformErrorEventUploadPreTransformErrorEvent( + UploadPreTransformErrorEventUploadPreTransformErrorEvent uploadPreTransformErrorEventUploadPreTransformErrorEvent + ) + : base(uploadPreTransformErrorEventUploadPreTransformErrorEvent) { } +#pragma warning restore CS8618 + + public UploadPreTransformErrorEventUploadPreTransformErrorEvent( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("upload.pre-transform.error"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UploadPreTransformErrorEventUploadPreTransformErrorEvent( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UploadPreTransformErrorEventUploadPreTransformErrorEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class UploadPreTransformErrorEventUploadPreTransformErrorEventFromRaw + : IFromRawJson +{ + /// + public UploadPreTransformErrorEventUploadPreTransformErrorEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) => UploadPreTransformErrorEventUploadPreTransformErrorEvent.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter< + UploadPreTransformErrorEventUploadPreTransformErrorEventData, + UploadPreTransformErrorEventUploadPreTransformErrorEventDataFromRaw + >) +)] +public sealed record class UploadPreTransformErrorEventUploadPreTransformErrorEventData : JsonModel +{ + /// + /// Name of the file. + /// + public required string Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("name"); + } + init { this._rawData.Set("name", value); } + } + + /// + /// Path of the file. + /// + public required string Path + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("path"); + } + init { this._rawData.Set("path", value); } + } + + public required UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformation Transformation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "transformation" + ); + } + init { this._rawData.Set("transformation", value); } + } + + /// + public override void Validate() + { + _ = this.Name; + _ = this.Path; + this.Transformation.Validate(); + } + + public UploadPreTransformErrorEventUploadPreTransformErrorEventData() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UploadPreTransformErrorEventUploadPreTransformErrorEventData( + UploadPreTransformErrorEventUploadPreTransformErrorEventData uploadPreTransformErrorEventUploadPreTransformErrorEventData + ) + : base(uploadPreTransformErrorEventUploadPreTransformErrorEventData) { } +#pragma warning restore CS8618 + + public UploadPreTransformErrorEventUploadPreTransformErrorEventData( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UploadPreTransformErrorEventUploadPreTransformErrorEventData( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UploadPreTransformErrorEventUploadPreTransformErrorEventData FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class UploadPreTransformErrorEventUploadPreTransformErrorEventDataFromRaw + : IFromRawJson +{ + /// + public UploadPreTransformErrorEventUploadPreTransformErrorEventData FromRawUnchecked( + IReadOnlyDictionary rawData + ) => UploadPreTransformErrorEventUploadPreTransformErrorEventData.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter< + UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformation, + UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationFromRaw + >) +)] +public sealed record class UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformation + : JsonModel +{ + public required UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError Error + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "error" + ); + } + init { this._rawData.Set("error", value); } + } + + /// + public override void Validate() + { + this.Error.Validate(); + } + + public UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformation() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformation( + UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformation uploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformation + ) + : base(uploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformation) { } +#pragma warning restore CS8618 + + public UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformation( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformation( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformation FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformation( + UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError error + ) + : this() + { + this.Error = error; + } +} + +class UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationFromRaw + : IFromRawJson +{ + /// + public UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformation FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformation.FromRawUnchecked( + rawData + ); +} + +[JsonConverter( + typeof(JsonModelConverter< + UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError, + UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationErrorFromRaw + >) +)] +public sealed record class UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError + : JsonModel +{ + /// + /// Reason for the pre-transformation failure. + /// + public required string Reason + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("reason"); + } + init { this._rawData.Set("reason", value); } + } + + /// + public override void Validate() + { + _ = this.Reason; + } + + public UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError uploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError + ) + : base(uploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError) { } +#pragma warning restore CS8618 + + public UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError( + string reason + ) + : this() + { + this.Reason = reason; + } +} + +class UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationErrorFromRaw + : IFromRawJson +{ + /// + public UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + UploadPreTransformErrorEventUploadPreTransformErrorEventDataTransformationError.FromRawUnchecked( + rawData + ); +} + +[JsonConverter( + typeof(JsonModelConverter< + UploadPreTransformErrorEventUploadPreTransformErrorEventRequest, + UploadPreTransformErrorEventUploadPreTransformErrorEventRequestFromRaw + >) +)] +public sealed record class UploadPreTransformErrorEventUploadPreTransformErrorEventRequest + : JsonModel +{ + /// + /// The requested pre-transformation string. + /// + public required string Transformation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("transformation"); + } + init { this._rawData.Set("transformation", value); } + } + + /// + /// Unique identifier for the originating request. + /// + public required string XRequestID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("x_request_id"); + } + init { this._rawData.Set("x_request_id", value); } + } + + /// + public override void Validate() + { + _ = this.Transformation; + _ = this.XRequestID; + } + + public UploadPreTransformErrorEventUploadPreTransformErrorEventRequest() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UploadPreTransformErrorEventUploadPreTransformErrorEventRequest( + UploadPreTransformErrorEventUploadPreTransformErrorEventRequest uploadPreTransformErrorEventUploadPreTransformErrorEventRequest + ) + : base(uploadPreTransformErrorEventUploadPreTransformErrorEventRequest) { } +#pragma warning restore CS8618 + + public UploadPreTransformErrorEventUploadPreTransformErrorEventRequest( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UploadPreTransformErrorEventUploadPreTransformErrorEventRequest( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UploadPreTransformErrorEventUploadPreTransformErrorEventRequest FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class UploadPreTransformErrorEventUploadPreTransformErrorEventRequestFromRaw + : IFromRawJson +{ + /// + public UploadPreTransformErrorEventUploadPreTransformErrorEventRequest FromRawUnchecked( + IReadOnlyDictionary rawData + ) => UploadPreTransformErrorEventUploadPreTransformErrorEventRequest.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Webhooks/UploadPreTransformSuccessEvent.cs b/src/Imagekit/Models/Webhooks/UploadPreTransformSuccessEvent.cs new file mode 100644 index 00000000..29af5e37 --- /dev/null +++ b/src/Imagekit/Models/Webhooks/UploadPreTransformSuccessEvent.cs @@ -0,0 +1,1406 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using Files = Imagekit.Models.Files; +using System = System; + +namespace Imagekit.Models.Webhooks; + +/// +/// Triggered when a pre-transformation completes successfully. The file has been +/// processed with the requested transformation and is now available in the Media +/// Library. +/// +[JsonConverter( + typeof(JsonModelConverter< + UploadPreTransformSuccessEvent, + UploadPreTransformSuccessEventFromRaw + >) +)] +public sealed record class UploadPreTransformSuccessEvent : JsonModel +{ + /// + /// Unique identifier for the event. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + /// + /// The type of webhook event. + /// + public required string Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Timestamp of when the event occurred in ISO8601 format. + /// + public required System::DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + /// + /// Object containing details of a successful upload. + /// + public required UploadPreTransformSuccessEventUploadPreTransformSuccessEventData Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "data" + ); + } + init { this._rawData.Set("data", value); } + } + + public required UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest Request + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "request" + ); + } + init { this._rawData.Set("request", value); } + } + + public static implicit operator BaseWebhookEvent( + UploadPreTransformSuccessEvent uploadPreTransformSuccessEvent + ) => + new() + { + ID = uploadPreTransformSuccessEvent.ID, + Type = uploadPreTransformSuccessEvent.Type, + }; + + /// + public override void Validate() + { + _ = this.ID; + _ = this.Type; + _ = this.CreatedAt; + this.Data.Validate(); + this.Request.Validate(); + } + + public UploadPreTransformSuccessEvent() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UploadPreTransformSuccessEvent( + UploadPreTransformSuccessEvent uploadPreTransformSuccessEvent + ) + : base(uploadPreTransformSuccessEvent) { } +#pragma warning restore CS8618 + + public UploadPreTransformSuccessEvent(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UploadPreTransformSuccessEvent(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UploadPreTransformSuccessEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class UploadPreTransformSuccessEventFromRaw : IFromRawJson +{ + /// + public UploadPreTransformSuccessEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) => UploadPreTransformSuccessEvent.FromRawUnchecked(rawData); +} + +/// +/// Triggered when a pre-transformation completes successfully. The file has been +/// processed with the requested transformation and is now available in the Media +/// Library. +/// +[JsonConverter( + typeof(JsonModelConverter< + UploadPreTransformSuccessEventUploadPreTransformSuccessEvent, + UploadPreTransformSuccessEventUploadPreTransformSuccessEventFromRaw + >) +)] +public sealed record class UploadPreTransformSuccessEventUploadPreTransformSuccessEvent : JsonModel +{ + /// + /// Timestamp of when the event occurred in ISO8601 format. + /// + public required System::DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + /// + /// Object containing details of a successful upload. + /// + public required UploadPreTransformSuccessEventUploadPreTransformSuccessEventData Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "data" + ); + } + init { this._rawData.Set("data", value); } + } + + public required UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest Request + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "request" + ); + } + init { this._rawData.Set("request", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + public override void Validate() + { + _ = this.CreatedAt; + this.Data.Validate(); + this.Request.Validate(); + if ( + !JsonElement.DeepEquals( + this.Type, + JsonSerializer.SerializeToElement("upload.pre-transform.success") + ) + ) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + } + + public UploadPreTransformSuccessEventUploadPreTransformSuccessEvent() + { + this.Type = JsonSerializer.SerializeToElement("upload.pre-transform.success"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UploadPreTransformSuccessEventUploadPreTransformSuccessEvent( + UploadPreTransformSuccessEventUploadPreTransformSuccessEvent uploadPreTransformSuccessEventUploadPreTransformSuccessEvent + ) + : base(uploadPreTransformSuccessEventUploadPreTransformSuccessEvent) { } +#pragma warning restore CS8618 + + public UploadPreTransformSuccessEventUploadPreTransformSuccessEvent( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("upload.pre-transform.success"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UploadPreTransformSuccessEventUploadPreTransformSuccessEvent( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UploadPreTransformSuccessEventUploadPreTransformSuccessEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class UploadPreTransformSuccessEventUploadPreTransformSuccessEventFromRaw + : IFromRawJson +{ + /// + public UploadPreTransformSuccessEventUploadPreTransformSuccessEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) => UploadPreTransformSuccessEventUploadPreTransformSuccessEvent.FromRawUnchecked(rawData); +} + +/// +/// Object containing details of a successful upload. +/// +[JsonConverter( + typeof(JsonModelConverter< + UploadPreTransformSuccessEventUploadPreTransformSuccessEventData, + UploadPreTransformSuccessEventUploadPreTransformSuccessEventDataFromRaw + >) +)] +public sealed record class UploadPreTransformSuccessEventUploadPreTransformSuccessEventData + : JsonModel +{ + /// + /// An array of tags assigned to the uploaded file by auto tagging. + /// + public IReadOnlyList? AITags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("AITags"); + } + init + { + this._rawData.Set?>( + "AITags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// The audio codec used in the video (only for video). + /// + public string? AudioCodec + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("audioCodec"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("audioCodec", value); + } + } + + /// + /// The bit rate of the video in kbps (only for video). + /// + public long? BitRate + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("bitRate"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("bitRate", value); + } + } + + /// + /// Value of custom coordinates associated with the image in the format `x,y,width,height`. + /// If `customCoordinates` are not defined, then it is `null`. Send `customCoordinates` + /// in `responseFields` in API request to get the value of this field. + /// + public string? CustomCoordinates + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("customCoordinates"); + } + init { this._rawData.Set("customCoordinates", value); } + } + + /// + /// A key-value data associated with the asset. Use `responseField` in API request + /// to get `customMetadata` in the upload API response. Before setting any custom + /// metadata on an asset, you have to create the field using custom metadata fields + /// API. Send `customMetadata` in `responseFields` in API request to get the value + /// of this field. + /// + public IReadOnlyDictionary? CustomMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "customMetadata" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "customMetadata", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Optional text to describe the contents of the file. Can be set by the user + /// or the ai-auto-description extension. + /// + public string? Description + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("description"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("description", value); + } + } + + /// + /// The duration of the video in seconds (only for video). + /// + public long? Duration + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("duration"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("duration", value); + } + } + + /// + /// Consolidated embedded metadata associated with the file. It includes exif, + /// iptc, and xmp data. Send `embeddedMetadata` in `responseFields` in API request + /// to get embeddedMetadata in the upload API response. + /// + public IReadOnlyDictionary? EmbeddedMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "embeddedMetadata" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "embeddedMetadata", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Extension names with their processing status at the time of completion of + /// the request. It could have one of the following status values: + /// + /// `success`: The extension has been successfully applied. `failed`: The + /// extension has failed and will not be retried. `pending`: The extension will + /// finish processing in some time. On completion, the final status (success + /// / failed) will be sent to the `webhookUrl` provided. + /// + /// If no extension was requested, then this parameter is not returned. + /// + public ExtensionStatus? ExtensionStatus + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("extensionStatus"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("extensionStatus", value); + } + } + + /// + /// Unique fileId. Store this fileld in your database, as this will be used to + /// perform update action on this file. + /// + public string? FileID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("fileId"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("fileId", value); + } + } + + /// + /// The relative path of the file in the media library e.g. `/marketing-assets/new-banner.jpg`. + /// + public string? FilePath + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("filePath"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("filePath", value); + } + } + + /// + /// Type of the uploaded file. Possible values are `image`, `non-image`. + /// + public string? FileType + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("fileType"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("fileType", value); + } + } + + /// + /// Height of the image in pixels (Only for images) + /// + public double? Height + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("height"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("height", value); + } + } + + /// + /// Is the file marked as private. It can be either `true` or `false`. Send `isPrivateFile` + /// in `responseFields` in API request to get the value of this field. + /// + public bool? IsPrivateFile + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("isPrivateFile"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("isPrivateFile", value); + } + } + + /// + /// Is the file published or in draft state. It can be either `true` or `false`. + /// Send `isPublished` in `responseFields` in API request to get the value of + /// this field. + /// + public bool? IsPublished + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("isPublished"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("isPublished", value); + } + } + + /// + /// Legacy metadata. Send `metadata` in `responseFields` in API request to get + /// metadata in the upload API response. + /// + public Files::FileMetadata? Metadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("metadata"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("metadata", value); + } + } + + /// + /// Name of the asset. + /// + public string? Name + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("name"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("name", value); + } + } + + /// + /// This field is included in the response only if the Path policy feature is + /// available in the plan. It contains schema definitions for the custom metadata + /// fields selected for the specified file path. Field selection can only be + /// done when the Path policy feature is enabled. + /// + /// Keys are the names of the custom metadata fields; the value object + /// has details about the custom metadata schema. + /// + public IReadOnlyDictionary? SelectedFieldsSchema + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + FrozenDictionary + >("selectedFieldsSchema"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "selectedFieldsSchema", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Size of the image file in Bytes. + /// + public double? Size + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("size"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("size", value); + } + } + + /// + /// The array of tags associated with the asset. If no tags are set, it will be + /// `null`. Send `tags` in `responseFields` in API request to get the value of + /// this field. + /// + public IReadOnlyList? Tags + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("tags"); + } + init + { + this._rawData.Set?>( + "tags", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// In the case of an image, a small thumbnail URL. + /// + public string? ThumbnailUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("thumbnailUrl"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("thumbnailUrl", value); + } + } + + /// + /// A publicly accessible URL of the file. + /// + public string? Url + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("url"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("url", value); + } + } + + /// + /// An object containing the file or file version's `id` (versionId) and `name`. + /// + public VersionInfo? VersionInfo + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("versionInfo"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("versionInfo", value); + } + } + + /// + /// The video codec used in the video (only for video). + /// + public string? VideoCodec + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("videoCodec"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("videoCodec", value); + } + } + + /// + /// Width of the image in pixels (Only for Images) + /// + public double? Width + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("width"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("width", value); + } + } + + /// + public override void Validate() + { + foreach (var item in this.AITags ?? []) + { + item.Validate(); + } + _ = this.AudioCodec; + _ = this.BitRate; + _ = this.CustomCoordinates; + _ = this.CustomMetadata; + _ = this.Description; + _ = this.Duration; + _ = this.EmbeddedMetadata; + this.ExtensionStatus?.Validate(); + _ = this.FileID; + _ = this.FilePath; + _ = this.FileType; + _ = this.Height; + _ = this.IsPrivateFile; + _ = this.IsPublished; + this.Metadata?.Validate(); + _ = this.Name; + if (this.SelectedFieldsSchema != null) + { + foreach (var item in this.SelectedFieldsSchema.Values) + { + item.Validate(); + } + } + _ = this.Size; + _ = this.Tags; + _ = this.ThumbnailUrl; + _ = this.Url; + this.VersionInfo?.Validate(); + _ = this.VideoCodec; + _ = this.Width; + } + + public UploadPreTransformSuccessEventUploadPreTransformSuccessEventData() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UploadPreTransformSuccessEventUploadPreTransformSuccessEventData( + UploadPreTransformSuccessEventUploadPreTransformSuccessEventData uploadPreTransformSuccessEventUploadPreTransformSuccessEventData + ) + : base(uploadPreTransformSuccessEventUploadPreTransformSuccessEventData) { } +#pragma warning restore CS8618 + + public UploadPreTransformSuccessEventUploadPreTransformSuccessEventData( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UploadPreTransformSuccessEventUploadPreTransformSuccessEventData( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UploadPreTransformSuccessEventUploadPreTransformSuccessEventData FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class UploadPreTransformSuccessEventUploadPreTransformSuccessEventDataFromRaw + : IFromRawJson +{ + /// + public UploadPreTransformSuccessEventUploadPreTransformSuccessEventData FromRawUnchecked( + IReadOnlyDictionary rawData + ) => UploadPreTransformSuccessEventUploadPreTransformSuccessEventData.FromRawUnchecked(rawData); +} + +/// +/// Extension names with their processing status at the time of completion of the +/// request. It could have one of the following status values: +/// +/// `success`: The extension has been successfully applied. `failed`: The extension +/// has failed and will not be retried. `pending`: The extension will finish processing +/// in some time. On completion, the final status (success / failed) will be sent +/// to the `webhookUrl` provided. +/// +/// If no extension was requested, then this parameter is not returned. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class ExtensionStatus : JsonModel +{ + public ApiEnum? AIAutoDescription + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "ai-auto-description" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ai-auto-description", value); + } + } + + public ApiEnum? AITasks + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("ai-tasks"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ai-tasks", value); + } + } + + public ApiEnum? AwsAutoTagging + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "aws-auto-tagging" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("aws-auto-tagging", value); + } + } + + public ApiEnum? GoogleAutoTagging + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "google-auto-tagging" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("google-auto-tagging", value); + } + } + + public ApiEnum? RemoveBg + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("remove-bg"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("remove-bg", value); + } + } + + /// + public override void Validate() + { + this.AIAutoDescription?.Validate(); + this.AITasks?.Validate(); + this.AwsAutoTagging?.Validate(); + this.GoogleAutoTagging?.Validate(); + this.RemoveBg?.Validate(); + } + + public ExtensionStatus() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExtensionStatus(ExtensionStatus extensionStatus) + : base(extensionStatus) { } +#pragma warning restore CS8618 + + public ExtensionStatus(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExtensionStatus(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExtensionStatus FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ExtensionStatusFromRaw : IFromRawJson +{ + /// + public ExtensionStatus FromRawUnchecked(IReadOnlyDictionary rawData) => + ExtensionStatus.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(AIAutoDescriptionConverter))] +public enum AIAutoDescription +{ + Success, + Pending, + Failed, +} + +sealed class AIAutoDescriptionConverter : JsonConverter +{ + public override AIAutoDescription Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "success" => AIAutoDescription.Success, + "pending" => AIAutoDescription.Pending, + "failed" => AIAutoDescription.Failed, + _ => (AIAutoDescription)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + AIAutoDescription value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + AIAutoDescription.Success => "success", + AIAutoDescription.Pending => "pending", + AIAutoDescription.Failed => "failed", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +[JsonConverter(typeof(AITasksConverter))] +public enum AITasks +{ + Success, + Pending, + Failed, +} + +sealed class AITasksConverter : JsonConverter +{ + public override AITasks Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "success" => AITasks.Success, + "pending" => AITasks.Pending, + "failed" => AITasks.Failed, + _ => (AITasks)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, AITasks value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + AITasks.Success => "success", + AITasks.Pending => "pending", + AITasks.Failed => "failed", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +[JsonConverter(typeof(AwsAutoTaggingConverter))] +public enum AwsAutoTagging +{ + Success, + Pending, + Failed, +} + +sealed class AwsAutoTaggingConverter : JsonConverter +{ + public override AwsAutoTagging Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "success" => AwsAutoTagging.Success, + "pending" => AwsAutoTagging.Pending, + "failed" => AwsAutoTagging.Failed, + _ => (AwsAutoTagging)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + AwsAutoTagging value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + AwsAutoTagging.Success => "success", + AwsAutoTagging.Pending => "pending", + AwsAutoTagging.Failed => "failed", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +[JsonConverter(typeof(GoogleAutoTaggingConverter))] +public enum GoogleAutoTagging +{ + Success, + Pending, + Failed, +} + +sealed class GoogleAutoTaggingConverter : JsonConverter +{ + public override GoogleAutoTagging Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "success" => GoogleAutoTagging.Success, + "pending" => GoogleAutoTagging.Pending, + "failed" => GoogleAutoTagging.Failed, + _ => (GoogleAutoTagging)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + GoogleAutoTagging value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + GoogleAutoTagging.Success => "success", + GoogleAutoTagging.Pending => "pending", + GoogleAutoTagging.Failed => "failed", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +[JsonConverter(typeof(RemoveBgConverter))] +public enum RemoveBg +{ + Success, + Pending, + Failed, +} + +sealed class RemoveBgConverter : JsonConverter +{ + public override RemoveBg Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "success" => RemoveBg.Success, + "pending" => RemoveBg.Pending, + "failed" => RemoveBg.Failed, + _ => (RemoveBg)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, RemoveBg value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + RemoveBg.Success => "success", + RemoveBg.Pending => "pending", + RemoveBg.Failed => "failed", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +[JsonConverter( + typeof(JsonModelConverter< + UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest, + UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequestFromRaw + >) +)] +public sealed record class UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest + : JsonModel +{ + /// + /// The requested pre-transformation string. + /// + public required string Transformation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("transformation"); + } + init { this._rawData.Set("transformation", value); } + } + + /// + /// Unique identifier for the originating request. + /// + public required string XRequestID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("x_request_id"); + } + init { this._rawData.Set("x_request_id", value); } + } + + /// + public override void Validate() + { + _ = this.Transformation; + _ = this.XRequestID; + } + + public UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest( + UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest uploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest + ) + : base(uploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest) { } +#pragma warning restore CS8618 + + public UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequestFromRaw + : IFromRawJson +{ + /// + public UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + UploadPreTransformSuccessEventUploadPreTransformSuccessEventRequest.FromRawUnchecked( + rawData + ); +} diff --git a/src/Imagekit/Models/Webhooks/VideoTransformationAcceptedEvent.cs b/src/Imagekit/Models/Webhooks/VideoTransformationAcceptedEvent.cs new file mode 100644 index 00000000..861649a2 --- /dev/null +++ b/src/Imagekit/Models/Webhooks/VideoTransformationAcceptedEvent.cs @@ -0,0 +1,1143 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models.Webhooks; + +/// +/// Triggered when a new video transformation request is accepted for processing. +/// This event confirms that ImageKit has received and queued your transformation +/// request. Use this for debugging and tracking transformation lifecycle. +/// +[JsonConverter( + typeof(JsonModelConverter< + VideoTransformationAcceptedEvent, + VideoTransformationAcceptedEventFromRaw + >) +)] +public sealed record class VideoTransformationAcceptedEvent : JsonModel +{ + /// + /// Unique identifier for the event. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + /// + /// The type of webhook event. + /// + public required string Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Timestamp when the event was created in ISO8601 format. + /// + public required System::DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + public required VideoTransformationAcceptedEventVideoTransformationAcceptedEventData Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "data" + ); + } + init { this._rawData.Set("data", value); } + } + + /// + /// Information about the original request that triggered the video transformation. + /// + public required VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest Request + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "request" + ); + } + init { this._rawData.Set("request", value); } + } + + public static implicit operator BaseWebhookEvent( + VideoTransformationAcceptedEvent videoTransformationAcceptedEvent + ) => + new() + { + ID = videoTransformationAcceptedEvent.ID, + Type = videoTransformationAcceptedEvent.Type, + }; + + /// + public override void Validate() + { + _ = this.ID; + _ = this.Type; + _ = this.CreatedAt; + this.Data.Validate(); + this.Request.Validate(); + } + + public VideoTransformationAcceptedEvent() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoTransformationAcceptedEvent( + VideoTransformationAcceptedEvent videoTransformationAcceptedEvent + ) + : base(videoTransformationAcceptedEvent) { } +#pragma warning restore CS8618 + + public VideoTransformationAcceptedEvent(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoTransformationAcceptedEvent(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoTransformationAcceptedEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class VideoTransformationAcceptedEventFromRaw : IFromRawJson +{ + /// + public VideoTransformationAcceptedEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) => VideoTransformationAcceptedEvent.FromRawUnchecked(rawData); +} + +/// +/// Triggered when a new video transformation request is accepted for processing. +/// This event confirms that ImageKit has received and queued your transformation +/// request. Use this for debugging and tracking transformation lifecycle. +/// +[JsonConverter( + typeof(JsonModelConverter< + VideoTransformationAcceptedEventVideoTransformationAcceptedEvent, + VideoTransformationAcceptedEventVideoTransformationAcceptedEventFromRaw + >) +)] +public sealed record class VideoTransformationAcceptedEventVideoTransformationAcceptedEvent + : JsonModel +{ + /// + /// Timestamp when the event was created in ISO8601 format. + /// + public required System::DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + public required VideoTransformationAcceptedEventVideoTransformationAcceptedEventData Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "data" + ); + } + init { this._rawData.Set("data", value); } + } + + /// + /// Information about the original request that triggered the video transformation. + /// + public required VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest Request + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "request" + ); + } + init { this._rawData.Set("request", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + public override void Validate() + { + _ = this.CreatedAt; + this.Data.Validate(); + this.Request.Validate(); + if ( + !JsonElement.DeepEquals( + this.Type, + JsonSerializer.SerializeToElement("video.transformation.accepted") + ) + ) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + } + + public VideoTransformationAcceptedEventVideoTransformationAcceptedEvent() + { + this.Type = JsonSerializer.SerializeToElement("video.transformation.accepted"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoTransformationAcceptedEventVideoTransformationAcceptedEvent( + VideoTransformationAcceptedEventVideoTransformationAcceptedEvent videoTransformationAcceptedEventVideoTransformationAcceptedEvent + ) + : base(videoTransformationAcceptedEventVideoTransformationAcceptedEvent) { } +#pragma warning restore CS8618 + + public VideoTransformationAcceptedEventVideoTransformationAcceptedEvent( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("video.transformation.accepted"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoTransformationAcceptedEventVideoTransformationAcceptedEvent( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoTransformationAcceptedEventVideoTransformationAcceptedEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class VideoTransformationAcceptedEventVideoTransformationAcceptedEventFromRaw + : IFromRawJson +{ + /// + public VideoTransformationAcceptedEventVideoTransformationAcceptedEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) => VideoTransformationAcceptedEventVideoTransformationAcceptedEvent.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter< + VideoTransformationAcceptedEventVideoTransformationAcceptedEventData, + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataFromRaw + >) +)] +public sealed record class VideoTransformationAcceptedEventVideoTransformationAcceptedEventData + : JsonModel +{ + /// + /// Information about the source video asset being transformed. + /// + public required Asset Asset + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("asset"); + } + init { this._rawData.Set("asset", value); } + } + + /// + /// Base information about a video transformation request. + /// + public required VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation Transformation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "transformation" + ); + } + init { this._rawData.Set("transformation", value); } + } + + /// + public override void Validate() + { + this.Asset.Validate(); + this.Transformation.Validate(); + } + + public VideoTransformationAcceptedEventVideoTransformationAcceptedEventData() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoTransformationAcceptedEventVideoTransformationAcceptedEventData( + VideoTransformationAcceptedEventVideoTransformationAcceptedEventData videoTransformationAcceptedEventVideoTransformationAcceptedEventData + ) + : base(videoTransformationAcceptedEventVideoTransformationAcceptedEventData) { } +#pragma warning restore CS8618 + + public VideoTransformationAcceptedEventVideoTransformationAcceptedEventData( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoTransformationAcceptedEventVideoTransformationAcceptedEventData( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoTransformationAcceptedEventVideoTransformationAcceptedEventData FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataFromRaw + : IFromRawJson +{ + /// + public VideoTransformationAcceptedEventVideoTransformationAcceptedEventData FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + VideoTransformationAcceptedEventVideoTransformationAcceptedEventData.FromRawUnchecked( + rawData + ); +} + +/// +/// Information about the source video asset being transformed. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Asset : JsonModel +{ + /// + /// URL to download or access the source video file. + /// + public required string Url + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("url"); + } + init { this._rawData.Set("url", value); } + } + + /// + public override void Validate() + { + _ = this.Url; + } + + public Asset() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Asset(Asset asset) + : base(asset) { } +#pragma warning restore CS8618 + + public Asset(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Asset(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Asset FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public Asset(string url) + : this() + { + this.Url = url; + } +} + +class AssetFromRaw : IFromRawJson +{ + /// + public Asset FromRawUnchecked(IReadOnlyDictionary rawData) => + Asset.FromRawUnchecked(rawData); +} + +/// +/// Base information about a video transformation request. +/// +[JsonConverter( + typeof(JsonModelConverter< + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation, + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationFromRaw + >) +)] +public sealed record class VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation + : JsonModel +{ + /// + /// Type of video transformation: - `video-transformation`: Standard video processing + /// (resize, format conversion, etc.) - `gif-to-video`: Convert animated GIF + /// to video format - `video-thumbnail`: Generate thumbnail image from video + /// + public required ApiEnum< + string, + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType + > Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass< + ApiEnum< + string, + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType + > + >("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Configuration options for video transformations. + /// + public Options? Options + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("options"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("options", value); + } + } + + /// + public override void Validate() + { + this.Type.Validate(); + this.Options?.Validate(); + } + + public VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation( + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation videoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation + ) + : base(videoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation) + { } +#pragma warning restore CS8618 + + public VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation( + ApiEnum< + string, + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType + > type + ) + : this() + { + this.Type = type; + } +} + +class VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationFromRaw + : IFromRawJson +{ + /// + public VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformation.FromRawUnchecked( + rawData + ); +} + +/// +/// Type of video transformation: - `video-transformation`: Standard video processing +/// (resize, format conversion, etc.) - `gif-to-video`: Convert animated GIF to video +/// format - `video-thumbnail`: Generate thumbnail image from video +/// +[JsonConverter( + typeof(VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationTypeConverter) +)] +public enum VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType +{ + VideoTransformation, + GifToVideo, + VideoThumbnail, +} + +sealed class VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationTypeConverter + : JsonConverter +{ + public override VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "video-transformation" => + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation, + "gif-to-video" => + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.GifToVideo, + "video-thumbnail" => + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoThumbnail, + _ => + (VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType)( + -1 + ), + }; + } + + public override void Write( + Utf8JsonWriter writer, + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoTransformation => + "video-transformation", + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.GifToVideo => + "gif-to-video", + VideoTransformationAcceptedEventVideoTransformationAcceptedEventDataTransformationType.VideoThumbnail => + "video-thumbnail", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Configuration options for video transformations. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Options : JsonModel +{ + /// + /// Audio codec used for encoding (aac or opus). + /// + public ApiEnum? AudioCodec + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("audio_codec"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("audio_codec", value); + } + } + + /// + /// Whether to automatically rotate the video based on metadata. + /// + public bool? AutoRotate + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("auto_rotate"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("auto_rotate", value); + } + } + + /// + /// Output format for the transformed video or thumbnail. + /// + public ApiEnum? Format + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("format"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("format", value); + } + } + + /// + /// Quality setting for the output video. + /// + public long? Quality + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("quality"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("quality", value); + } + } + + /// + /// Streaming protocol for adaptive bitrate streaming. + /// + public ApiEnum? StreamProtocol + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>( + "stream_protocol" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("stream_protocol", value); + } + } + + /// + /// Array of quality representations for adaptive bitrate streaming. + /// + public IReadOnlyList? Variants + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("variants"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "variants", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Video codec used for encoding (h264, vp9, or av1). + /// + public ApiEnum? VideoCodec + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("video_codec"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("video_codec", value); + } + } + + /// + public override void Validate() + { + this.AudioCodec?.Validate(); + _ = this.AutoRotate; + this.Format?.Validate(); + _ = this.Quality; + this.StreamProtocol?.Validate(); + _ = this.Variants; + this.VideoCodec?.Validate(); + } + + public Options() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Options(Options options) + : base(options) { } +#pragma warning restore CS8618 + + public Options(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Options(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Options FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class OptionsFromRaw : IFromRawJson +{ + /// + public Options FromRawUnchecked(IReadOnlyDictionary rawData) => + Options.FromRawUnchecked(rawData); +} + +/// +/// Audio codec used for encoding (aac or opus). +/// +[JsonConverter(typeof(AudioCodecConverter))] +public enum AudioCodec +{ + Aac, + Opus, +} + +sealed class AudioCodecConverter : JsonConverter +{ + public override AudioCodec Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "aac" => AudioCodec.Aac, + "opus" => AudioCodec.Opus, + _ => (AudioCodec)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + AudioCodec value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + AudioCodec.Aac => "aac", + AudioCodec.Opus => "opus", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Output format for the transformed video or thumbnail. +/// +[JsonConverter(typeof(FormatConverter))] +public enum Format +{ + Mp4, + Webm, + Jpg, + Png, + Webp, +} + +sealed class FormatConverter : JsonConverter +{ + public override Format Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "mp4" => Format.Mp4, + "webm" => Format.Webm, + "jpg" => Format.Jpg, + "png" => Format.Png, + "webp" => Format.Webp, + _ => (Format)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, Format value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + Format.Mp4 => "mp4", + Format.Webm => "webm", + Format.Jpg => "jpg", + Format.Png => "png", + Format.Webp => "webp", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Streaming protocol for adaptive bitrate streaming. +/// +[JsonConverter(typeof(StreamProtocolConverter))] +public enum StreamProtocol +{ + Hls, + Dash, +} + +sealed class StreamProtocolConverter : JsonConverter +{ + public override StreamProtocol Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "HLS" => StreamProtocol.Hls, + "DASH" => StreamProtocol.Dash, + _ => (StreamProtocol)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + StreamProtocol value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + StreamProtocol.Hls => "HLS", + StreamProtocol.Dash => "DASH", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Video codec used for encoding (h264, vp9, or av1). +/// +[JsonConverter(typeof(VideoCodecConverter))] +public enum VideoCodec +{ + H264, + Vp9, + Av1, +} + +sealed class VideoCodecConverter : JsonConverter +{ + public override VideoCodec Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "h264" => VideoCodec.H264, + "vp9" => VideoCodec.Vp9, + "av1" => VideoCodec.Av1, + _ => (VideoCodec)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + VideoCodec value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + VideoCodec.H264 => "h264", + VideoCodec.Vp9 => "vp9", + VideoCodec.Av1 => "av1", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Information about the original request that triggered the video transformation. +/// +[JsonConverter( + typeof(JsonModelConverter< + VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest, + VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequestFromRaw + >) +)] +public sealed record class VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest + : JsonModel +{ + /// + /// Full URL of the transformation request that was submitted. + /// + public required string Url + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("url"); + } + init { this._rawData.Set("url", value); } + } + + /// + /// Unique identifier for the originating transformation request. + /// + public required string XRequestID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("x_request_id"); + } + init { this._rawData.Set("x_request_id", value); } + } + + /// + /// User-Agent header from the original request that triggered the transformation. + /// + public string? UserAgent + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("user_agent"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("user_agent", value); + } + } + + /// + public override void Validate() + { + _ = this.Url; + _ = this.XRequestID; + _ = this.UserAgent; + } + + public VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest( + VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest videoTransformationAcceptedEventVideoTransformationAcceptedEventRequest + ) + : base(videoTransformationAcceptedEventVideoTransformationAcceptedEventRequest) { } +#pragma warning restore CS8618 + + public VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequestFromRaw + : IFromRawJson +{ + /// + public VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + VideoTransformationAcceptedEventVideoTransformationAcceptedEventRequest.FromRawUnchecked( + rawData + ); +} diff --git a/src/Imagekit/Models/Webhooks/VideoTransformationErrorEvent.cs b/src/Imagekit/Models/Webhooks/VideoTransformationErrorEvent.cs new file mode 100644 index 00000000..8ffa0ff4 --- /dev/null +++ b/src/Imagekit/Models/Webhooks/VideoTransformationErrorEvent.cs @@ -0,0 +1,1411 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models.Webhooks; + +/// +/// Triggered when an error occurs during video encoding. Listen to this webhook +/// to log error reasons and debug issues. Check your origin and URL endpoint settings +/// if the reason is related to download failure. For other errors, contact ImageKit +/// support. +/// +[JsonConverter( + typeof(JsonModelConverter) +)] +public sealed record class VideoTransformationErrorEvent : JsonModel +{ + /// + /// Unique identifier for the event. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + /// + /// The type of webhook event. + /// + public required string Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Timestamp when the event was created in ISO8601 format. + /// + public required System::DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + public required VideoTransformationErrorEventVideoTransformationErrorEventData Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "data" + ); + } + init { this._rawData.Set("data", value); } + } + + /// + /// Information about the original request that triggered the video transformation. + /// + public required VideoTransformationErrorEventVideoTransformationErrorEventRequest Request + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "request" + ); + } + init { this._rawData.Set("request", value); } + } + + public static implicit operator BaseWebhookEvent( + VideoTransformationErrorEvent videoTransformationErrorEvent + ) => new() { ID = videoTransformationErrorEvent.ID, Type = videoTransformationErrorEvent.Type }; + + /// + public override void Validate() + { + _ = this.ID; + _ = this.Type; + _ = this.CreatedAt; + this.Data.Validate(); + this.Request.Validate(); + } + + public VideoTransformationErrorEvent() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoTransformationErrorEvent( + VideoTransformationErrorEvent videoTransformationErrorEvent + ) + : base(videoTransformationErrorEvent) { } +#pragma warning restore CS8618 + + public VideoTransformationErrorEvent(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoTransformationErrorEvent(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoTransformationErrorEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class VideoTransformationErrorEventFromRaw : IFromRawJson +{ + /// + public VideoTransformationErrorEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) => VideoTransformationErrorEvent.FromRawUnchecked(rawData); +} + +/// +/// Triggered when an error occurs during video encoding. Listen to this webhook +/// to log error reasons and debug issues. Check your origin and URL endpoint settings +/// if the reason is related to download failure. For other errors, contact ImageKit +/// support. +/// +[JsonConverter( + typeof(JsonModelConverter< + VideoTransformationErrorEventVideoTransformationErrorEvent, + VideoTransformationErrorEventVideoTransformationErrorEventFromRaw + >) +)] +public sealed record class VideoTransformationErrorEventVideoTransformationErrorEvent : JsonModel +{ + /// + /// Timestamp when the event was created in ISO8601 format. + /// + public required System::DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + public required VideoTransformationErrorEventVideoTransformationErrorEventData Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "data" + ); + } + init { this._rawData.Set("data", value); } + } + + /// + /// Information about the original request that triggered the video transformation. + /// + public required VideoTransformationErrorEventVideoTransformationErrorEventRequest Request + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "request" + ); + } + init { this._rawData.Set("request", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + public override void Validate() + { + _ = this.CreatedAt; + this.Data.Validate(); + this.Request.Validate(); + if ( + !JsonElement.DeepEquals( + this.Type, + JsonSerializer.SerializeToElement("video.transformation.error") + ) + ) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + } + + public VideoTransformationErrorEventVideoTransformationErrorEvent() + { + this.Type = JsonSerializer.SerializeToElement("video.transformation.error"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoTransformationErrorEventVideoTransformationErrorEvent( + VideoTransformationErrorEventVideoTransformationErrorEvent videoTransformationErrorEventVideoTransformationErrorEvent + ) + : base(videoTransformationErrorEventVideoTransformationErrorEvent) { } +#pragma warning restore CS8618 + + public VideoTransformationErrorEventVideoTransformationErrorEvent( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("video.transformation.error"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoTransformationErrorEventVideoTransformationErrorEvent( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoTransformationErrorEventVideoTransformationErrorEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class VideoTransformationErrorEventVideoTransformationErrorEventFromRaw + : IFromRawJson +{ + /// + public VideoTransformationErrorEventVideoTransformationErrorEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) => VideoTransformationErrorEventVideoTransformationErrorEvent.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter< + VideoTransformationErrorEventVideoTransformationErrorEventData, + VideoTransformationErrorEventVideoTransformationErrorEventDataFromRaw + >) +)] +public sealed record class VideoTransformationErrorEventVideoTransformationErrorEventData + : JsonModel +{ + /// + /// Information about the source video asset being transformed. + /// + public required VideoTransformationErrorEventVideoTransformationErrorEventDataAsset Asset + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "asset" + ); + } + init { this._rawData.Set("asset", value); } + } + + public required VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation Transformation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "transformation" + ); + } + init { this._rawData.Set("transformation", value); } + } + + /// + public override void Validate() + { + this.Asset.Validate(); + this.Transformation.Validate(); + } + + public VideoTransformationErrorEventVideoTransformationErrorEventData() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoTransformationErrorEventVideoTransformationErrorEventData( + VideoTransformationErrorEventVideoTransformationErrorEventData videoTransformationErrorEventVideoTransformationErrorEventData + ) + : base(videoTransformationErrorEventVideoTransformationErrorEventData) { } +#pragma warning restore CS8618 + + public VideoTransformationErrorEventVideoTransformationErrorEventData( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoTransformationErrorEventVideoTransformationErrorEventData( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoTransformationErrorEventVideoTransformationErrorEventData FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class VideoTransformationErrorEventVideoTransformationErrorEventDataFromRaw + : IFromRawJson +{ + /// + public VideoTransformationErrorEventVideoTransformationErrorEventData FromRawUnchecked( + IReadOnlyDictionary rawData + ) => VideoTransformationErrorEventVideoTransformationErrorEventData.FromRawUnchecked(rawData); +} + +/// +/// Information about the source video asset being transformed. +/// +[JsonConverter( + typeof(JsonModelConverter< + VideoTransformationErrorEventVideoTransformationErrorEventDataAsset, + VideoTransformationErrorEventVideoTransformationErrorEventDataAssetFromRaw + >) +)] +public sealed record class VideoTransformationErrorEventVideoTransformationErrorEventDataAsset + : JsonModel +{ + /// + /// URL to download or access the source video file. + /// + public required string Url + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("url"); + } + init { this._rawData.Set("url", value); } + } + + /// + public override void Validate() + { + _ = this.Url; + } + + public VideoTransformationErrorEventVideoTransformationErrorEventDataAsset() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoTransformationErrorEventVideoTransformationErrorEventDataAsset( + VideoTransformationErrorEventVideoTransformationErrorEventDataAsset videoTransformationErrorEventVideoTransformationErrorEventDataAsset + ) + : base(videoTransformationErrorEventVideoTransformationErrorEventDataAsset) { } +#pragma warning restore CS8618 + + public VideoTransformationErrorEventVideoTransformationErrorEventDataAsset( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoTransformationErrorEventVideoTransformationErrorEventDataAsset( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoTransformationErrorEventVideoTransformationErrorEventDataAsset FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public VideoTransformationErrorEventVideoTransformationErrorEventDataAsset(string url) + : this() + { + this.Url = url; + } +} + +class VideoTransformationErrorEventVideoTransformationErrorEventDataAssetFromRaw + : IFromRawJson +{ + /// + public VideoTransformationErrorEventVideoTransformationErrorEventDataAsset FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + VideoTransformationErrorEventVideoTransformationErrorEventDataAsset.FromRawUnchecked( + rawData + ); +} + +[JsonConverter( + typeof(JsonModelConverter< + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationFromRaw + >) +)] +public sealed record class VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation + : JsonModel +{ + /// + /// Type of video transformation: - `video-transformation`: Standard video processing + /// (resize, format conversion, etc.) - `gif-to-video`: Convert animated GIF + /// to video format - `video-thumbnail`: Generate thumbnail image from video + /// + public required ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType + > Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType + > + >("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Details about the transformation error. + /// + public VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationError? Error + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass( + "error" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("error", value); + } + } + + /// + /// Configuration options for video transformations. + /// + public VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions? Options + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass( + "options" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("options", value); + } + } + + /// + public override void Validate() + { + this.Type.Validate(); + this.Error?.Validate(); + this.Options?.Validate(); + } + + public VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation videoTransformationErrorEventVideoTransformationErrorEventDataTransformation + ) + : base(videoTransformationErrorEventVideoTransformationErrorEventDataTransformation) { } +#pragma warning restore CS8618 + + public VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation( + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType + > type + ) + : this() + { + this.Type = type; + } +} + +class VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationFromRaw + : IFromRawJson +{ + /// + public VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformation.FromRawUnchecked( + rawData + ); +} + +/// +/// Type of video transformation: - `video-transformation`: Standard video processing +/// (resize, format conversion, etc.) - `gif-to-video`: Convert animated GIF to video +/// format - `video-thumbnail`: Generate thumbnail image from video +/// +[JsonConverter( + typeof(VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationTypeConverter) +)] +public enum VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType +{ + VideoTransformation, + GifToVideo, + VideoThumbnail, +} + +sealed class VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationTypeConverter + : JsonConverter +{ + public override VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "video-transformation" => + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation, + "gif-to-video" => + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.GifToVideo, + "video-thumbnail" => + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoThumbnail, + _ => (VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType)( + -1 + ), + }; + } + + public override void Write( + Utf8JsonWriter writer, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoTransformation => + "video-transformation", + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.GifToVideo => + "gif-to-video", + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationType.VideoThumbnail => + "video-thumbnail", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Details about the transformation error. +/// +[JsonConverter( + typeof(JsonModelConverter< + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationError, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationErrorFromRaw + >) +)] +public sealed record class VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationError + : JsonModel +{ + /// + /// Specific reason for the transformation failure: - `encoding_failed`: Error + /// during video encoding process - `download_failed`: Could not download source + /// video - `internal_server_error`: Unexpected server error + /// + public required ApiEnum Reason + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass>("reason"); + } + init { this._rawData.Set("reason", value); } + } + + /// + public override void Validate() + { + this.Reason.Validate(); + } + + public VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationError() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationError( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationError videoTransformationErrorEventVideoTransformationErrorEventDataTransformationError + ) + : base(videoTransformationErrorEventVideoTransformationErrorEventDataTransformationError) + { } +#pragma warning restore CS8618 + + public VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationError( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationError( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationError FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationError( + ApiEnum reason + ) + : this() + { + this.Reason = reason; + } +} + +class VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationErrorFromRaw + : IFromRawJson +{ + /// + public VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationError FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationError.FromRawUnchecked( + rawData + ); +} + +/// +/// Specific reason for the transformation failure: - `encoding_failed`: Error during +/// video encoding process - `download_failed`: Could not download source video - +/// `internal_server_error`: Unexpected server error +/// +[JsonConverter(typeof(ReasonConverter))] +public enum Reason +{ + EncodingFailed, + DownloadFailed, + InternalServerError, +} + +sealed class ReasonConverter : JsonConverter +{ + public override Reason Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "encoding_failed" => Reason.EncodingFailed, + "download_failed" => Reason.DownloadFailed, + "internal_server_error" => Reason.InternalServerError, + _ => (Reason)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, Reason value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + Reason.EncodingFailed => "encoding_failed", + Reason.DownloadFailed => "download_failed", + Reason.InternalServerError => "internal_server_error", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Configuration options for video transformations. +/// +[JsonConverter( + typeof(JsonModelConverter< + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFromRaw + >) +)] +public sealed record class VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions + : JsonModel +{ + /// + /// Audio codec used for encoding (aac or opus). + /// + public ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec + >? AudioCodec + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec + > + >("audio_codec"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("audio_codec", value); + } + } + + /// + /// Whether to automatically rotate the video based on metadata. + /// + public bool? AutoRotate + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("auto_rotate"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("auto_rotate", value); + } + } + + /// + /// Output format for the transformed video or thumbnail. + /// + public ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat + >? Format + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat + > + >("format"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("format", value); + } + } + + /// + /// Quality setting for the output video. + /// + public long? Quality + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("quality"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("quality", value); + } + } + + /// + /// Streaming protocol for adaptive bitrate streaming. + /// + public ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol + >? StreamProtocol + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol + > + >("stream_protocol"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("stream_protocol", value); + } + } + + /// + /// Array of quality representations for adaptive bitrate streaming. + /// + public IReadOnlyList? Variants + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("variants"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "variants", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Video codec used for encoding (h264, vp9, or av1). + /// + public ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec + >? VideoCodec + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum< + string, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec + > + >("video_codec"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("video_codec", value); + } + } + + /// + public override void Validate() + { + this.AudioCodec?.Validate(); + _ = this.AutoRotate; + this.Format?.Validate(); + _ = this.Quality; + this.StreamProtocol?.Validate(); + _ = this.Variants; + this.VideoCodec?.Validate(); + } + + public VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions( + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions videoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions + ) + : base(videoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions) + { } +#pragma warning restore CS8618 + + public VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFromRaw + : IFromRawJson +{ + /// + public VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptions.FromRawUnchecked( + rawData + ); +} + +/// +/// Audio codec used for encoding (aac or opus). +/// +[JsonConverter( + typeof(VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodecConverter) +)] +public enum VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec +{ + Aac, + Opus, +} + +sealed class VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodecConverter + : JsonConverter +{ + public override VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "aac" => + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac, + "opus" => + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Opus, + _ => + (VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec)( + -1 + ), + }; + } + + public override void Write( + Utf8JsonWriter writer, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Aac => + "aac", + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsAudioCodec.Opus => + "opus", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Output format for the transformed video or thumbnail. +/// +[JsonConverter( + typeof(VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormatConverter) +)] +public enum VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat +{ + Mp4, + Webm, + Jpg, + Png, + Webp, +} + +sealed class VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormatConverter + : JsonConverter +{ + public override VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "mp4" => + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4, + "webm" => + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Webm, + "jpg" => + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Jpg, + "png" => + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Png, + "webp" => + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Webp, + _ => + (VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat)( + -1 + ), + }; + } + + public override void Write( + Utf8JsonWriter writer, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Mp4 => + "mp4", + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Webm => + "webm", + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Jpg => + "jpg", + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Png => + "png", + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsFormat.Webp => + "webp", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Streaming protocol for adaptive bitrate streaming. +/// +[JsonConverter( + typeof(VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocolConverter) +)] +public enum VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol +{ + Hls, + Dash, +} + +sealed class VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocolConverter + : JsonConverter +{ + public override VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "HLS" => + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls, + "DASH" => + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Dash, + _ => + (VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol)( + -1 + ), + }; + } + + public override void Write( + Utf8JsonWriter writer, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Hls => + "HLS", + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsStreamProtocol.Dash => + "DASH", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Video codec used for encoding (h264, vp9, or av1). +/// +[JsonConverter( + typeof(VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodecConverter) +)] +public enum VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec +{ + H264, + Vp9, + Av1, +} + +sealed class VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodecConverter + : JsonConverter +{ + public override VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "h264" => + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264, + "vp9" => + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.Vp9, + "av1" => + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.Av1, + _ => + (VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec)( + -1 + ), + }; + } + + public override void Write( + Utf8JsonWriter writer, + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.H264 => + "h264", + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.Vp9 => + "vp9", + VideoTransformationErrorEventVideoTransformationErrorEventDataTransformationOptionsVideoCodec.Av1 => + "av1", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Information about the original request that triggered the video transformation. +/// +[JsonConverter( + typeof(JsonModelConverter< + VideoTransformationErrorEventVideoTransformationErrorEventRequest, + VideoTransformationErrorEventVideoTransformationErrorEventRequestFromRaw + >) +)] +public sealed record class VideoTransformationErrorEventVideoTransformationErrorEventRequest + : JsonModel +{ + /// + /// Full URL of the transformation request that was submitted. + /// + public required string Url + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("url"); + } + init { this._rawData.Set("url", value); } + } + + /// + /// Unique identifier for the originating transformation request. + /// + public required string XRequestID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("x_request_id"); + } + init { this._rawData.Set("x_request_id", value); } + } + + /// + /// User-Agent header from the original request that triggered the transformation. + /// + public string? UserAgent + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("user_agent"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("user_agent", value); + } + } + + /// + public override void Validate() + { + _ = this.Url; + _ = this.XRequestID; + _ = this.UserAgent; + } + + public VideoTransformationErrorEventVideoTransformationErrorEventRequest() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoTransformationErrorEventVideoTransformationErrorEventRequest( + VideoTransformationErrorEventVideoTransformationErrorEventRequest videoTransformationErrorEventVideoTransformationErrorEventRequest + ) + : base(videoTransformationErrorEventVideoTransformationErrorEventRequest) { } +#pragma warning restore CS8618 + + public VideoTransformationErrorEventVideoTransformationErrorEventRequest( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoTransformationErrorEventVideoTransformationErrorEventRequest( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoTransformationErrorEventVideoTransformationErrorEventRequest FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class VideoTransformationErrorEventVideoTransformationErrorEventRequestFromRaw + : IFromRawJson +{ + /// + public VideoTransformationErrorEventVideoTransformationErrorEventRequest FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + VideoTransformationErrorEventVideoTransformationErrorEventRequest.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Models/Webhooks/VideoTransformationReadyEvent.cs b/src/Imagekit/Models/Webhooks/VideoTransformationReadyEvent.cs new file mode 100644 index 00000000..a3dffdaa --- /dev/null +++ b/src/Imagekit/Models/Webhooks/VideoTransformationReadyEvent.cs @@ -0,0 +1,1596 @@ +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; +using Imagekit.Core; +using Imagekit.Exceptions; +using System = System; + +namespace Imagekit.Models.Webhooks; + +/// +/// Triggered when video encoding is finished and the transformed resource is ready +/// to be served. This is the key event to listen for - update your database or CMS +/// flags when you receive this so your application can start showing the transformed +/// video to users. +/// +[JsonConverter( + typeof(JsonModelConverter) +)] +public sealed record class VideoTransformationReadyEvent : JsonModel +{ + /// + /// Unique identifier for the event. + /// + public required string ID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("id"); + } + init { this._rawData.Set("id", value); } + } + + /// + /// The type of webhook event. + /// + public required string Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Timestamp when the event was created in ISO8601 format. + /// + public required System::DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + public required VideoTransformationReadyEventVideoTransformationReadyEventData Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "data" + ); + } + init { this._rawData.Set("data", value); } + } + + /// + /// Information about the original request that triggered the video transformation. + /// + public required VideoTransformationReadyEventVideoTransformationReadyEventRequest Request + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "request" + ); + } + init { this._rawData.Set("request", value); } + } + + /// + /// Performance metrics for the transformation process. + /// + public Timings? Timings + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("timings"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("timings", value); + } + } + + public static implicit operator BaseWebhookEvent( + VideoTransformationReadyEvent videoTransformationReadyEvent + ) => new() { ID = videoTransformationReadyEvent.ID, Type = videoTransformationReadyEvent.Type }; + + /// + public override void Validate() + { + _ = this.ID; + _ = this.Type; + _ = this.CreatedAt; + this.Data.Validate(); + this.Request.Validate(); + this.Timings?.Validate(); + } + + public VideoTransformationReadyEvent() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoTransformationReadyEvent( + VideoTransformationReadyEvent videoTransformationReadyEvent + ) + : base(videoTransformationReadyEvent) { } +#pragma warning restore CS8618 + + public VideoTransformationReadyEvent(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoTransformationReadyEvent(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoTransformationReadyEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class VideoTransformationReadyEventFromRaw : IFromRawJson +{ + /// + public VideoTransformationReadyEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) => VideoTransformationReadyEvent.FromRawUnchecked(rawData); +} + +/// +/// Triggered when video encoding is finished and the transformed resource is ready +/// to be served. This is the key event to listen for - update your database or CMS +/// flags when you receive this so your application can start showing the transformed +/// video to users. +/// +[JsonConverter( + typeof(JsonModelConverter< + VideoTransformationReadyEventVideoTransformationReadyEvent, + VideoTransformationReadyEventVideoTransformationReadyEventFromRaw + >) +)] +public sealed record class VideoTransformationReadyEventVideoTransformationReadyEvent : JsonModel +{ + /// + /// Timestamp when the event was created in ISO8601 format. + /// + public required System::DateTimeOffset CreatedAt + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("created_at"); + } + init { this._rawData.Set("created_at", value); } + } + + public required VideoTransformationReadyEventVideoTransformationReadyEventData Data + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "data" + ); + } + init { this._rawData.Set("data", value); } + } + + /// + /// Information about the original request that triggered the video transformation. + /// + public required VideoTransformationReadyEventVideoTransformationReadyEventRequest Request + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "request" + ); + } + init { this._rawData.Set("request", value); } + } + + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Performance metrics for the transformation process. + /// + public Timings? Timings + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("timings"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("timings", value); + } + } + + /// + public override void Validate() + { + _ = this.CreatedAt; + this.Data.Validate(); + this.Request.Validate(); + if ( + !JsonElement.DeepEquals( + this.Type, + JsonSerializer.SerializeToElement("video.transformation.ready") + ) + ) + { + throw new ImageKitInvalidDataException("Invalid value given for constant"); + } + this.Timings?.Validate(); + } + + public VideoTransformationReadyEventVideoTransformationReadyEvent() + { + this.Type = JsonSerializer.SerializeToElement("video.transformation.ready"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoTransformationReadyEventVideoTransformationReadyEvent( + VideoTransformationReadyEventVideoTransformationReadyEvent videoTransformationReadyEventVideoTransformationReadyEvent + ) + : base(videoTransformationReadyEventVideoTransformationReadyEvent) { } +#pragma warning restore CS8618 + + public VideoTransformationReadyEventVideoTransformationReadyEvent( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("video.transformation.ready"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoTransformationReadyEventVideoTransformationReadyEvent( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoTransformationReadyEventVideoTransformationReadyEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class VideoTransformationReadyEventVideoTransformationReadyEventFromRaw + : IFromRawJson +{ + /// + public VideoTransformationReadyEventVideoTransformationReadyEvent FromRawUnchecked( + IReadOnlyDictionary rawData + ) => VideoTransformationReadyEventVideoTransformationReadyEvent.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter< + VideoTransformationReadyEventVideoTransformationReadyEventData, + VideoTransformationReadyEventVideoTransformationReadyEventDataFromRaw + >) +)] +public sealed record class VideoTransformationReadyEventVideoTransformationReadyEventData + : JsonModel +{ + /// + /// Information about the source video asset being transformed. + /// + public required VideoTransformationReadyEventVideoTransformationReadyEventDataAsset Asset + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "asset" + ); + } + init { this._rawData.Set("asset", value); } + } + + public required VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation Transformation + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "transformation" + ); + } + init { this._rawData.Set("transformation", value); } + } + + /// + public override void Validate() + { + this.Asset.Validate(); + this.Transformation.Validate(); + } + + public VideoTransformationReadyEventVideoTransformationReadyEventData() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoTransformationReadyEventVideoTransformationReadyEventData( + VideoTransformationReadyEventVideoTransformationReadyEventData videoTransformationReadyEventVideoTransformationReadyEventData + ) + : base(videoTransformationReadyEventVideoTransformationReadyEventData) { } +#pragma warning restore CS8618 + + public VideoTransformationReadyEventVideoTransformationReadyEventData( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoTransformationReadyEventVideoTransformationReadyEventData( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoTransformationReadyEventVideoTransformationReadyEventData FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class VideoTransformationReadyEventVideoTransformationReadyEventDataFromRaw + : IFromRawJson +{ + /// + public VideoTransformationReadyEventVideoTransformationReadyEventData FromRawUnchecked( + IReadOnlyDictionary rawData + ) => VideoTransformationReadyEventVideoTransformationReadyEventData.FromRawUnchecked(rawData); +} + +/// +/// Information about the source video asset being transformed. +/// +[JsonConverter( + typeof(JsonModelConverter< + VideoTransformationReadyEventVideoTransformationReadyEventDataAsset, + VideoTransformationReadyEventVideoTransformationReadyEventDataAssetFromRaw + >) +)] +public sealed record class VideoTransformationReadyEventVideoTransformationReadyEventDataAsset + : JsonModel +{ + /// + /// URL to download or access the source video file. + /// + public required string Url + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("url"); + } + init { this._rawData.Set("url", value); } + } + + /// + public override void Validate() + { + _ = this.Url; + } + + public VideoTransformationReadyEventVideoTransformationReadyEventDataAsset() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoTransformationReadyEventVideoTransformationReadyEventDataAsset( + VideoTransformationReadyEventVideoTransformationReadyEventDataAsset videoTransformationReadyEventVideoTransformationReadyEventDataAsset + ) + : base(videoTransformationReadyEventVideoTransformationReadyEventDataAsset) { } +#pragma warning restore CS8618 + + public VideoTransformationReadyEventVideoTransformationReadyEventDataAsset( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoTransformationReadyEventVideoTransformationReadyEventDataAsset( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoTransformationReadyEventVideoTransformationReadyEventDataAsset FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public VideoTransformationReadyEventVideoTransformationReadyEventDataAsset(string url) + : this() + { + this.Url = url; + } +} + +class VideoTransformationReadyEventVideoTransformationReadyEventDataAssetFromRaw + : IFromRawJson +{ + /// + public VideoTransformationReadyEventVideoTransformationReadyEventDataAsset FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + VideoTransformationReadyEventVideoTransformationReadyEventDataAsset.FromRawUnchecked( + rawData + ); +} + +[JsonConverter( + typeof(JsonModelConverter< + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationFromRaw + >) +)] +public sealed record class VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation + : JsonModel +{ + /// + /// Type of video transformation: - `video-transformation`: Standard video processing + /// (resize, format conversion, etc.) - `gif-to-video`: Convert animated GIF + /// to video format - `video-thumbnail`: Generate thumbnail image from video + /// + public required ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType + > Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType + > + >("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Configuration options for video transformations. + /// + public VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions? Options + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass( + "options" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("options", value); + } + } + + /// + /// Information about the transformed output video. + /// + public Output? Output + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("output"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("output", value); + } + } + + /// + public override void Validate() + { + this.Type.Validate(); + this.Options?.Validate(); + this.Output?.Validate(); + } + + public VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation videoTransformationReadyEventVideoTransformationReadyEventDataTransformation + ) + : base(videoTransformationReadyEventVideoTransformationReadyEventDataTransformation) { } +#pragma warning restore CS8618 + + public VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation( + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType + > type + ) + : this() + { + this.Type = type; + } +} + +class VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationFromRaw + : IFromRawJson +{ + /// + public VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformation.FromRawUnchecked( + rawData + ); +} + +/// +/// Type of video transformation: - `video-transformation`: Standard video processing +/// (resize, format conversion, etc.) - `gif-to-video`: Convert animated GIF to video +/// format - `video-thumbnail`: Generate thumbnail image from video +/// +[JsonConverter( + typeof(VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationTypeConverter) +)] +public enum VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType +{ + VideoTransformation, + GifToVideo, + VideoThumbnail, +} + +sealed class VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationTypeConverter + : JsonConverter +{ + public override VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "video-transformation" => + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation, + "gif-to-video" => + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.GifToVideo, + "video-thumbnail" => + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoThumbnail, + _ => (VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType)( + -1 + ), + }; + } + + public override void Write( + Utf8JsonWriter writer, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoTransformation => + "video-transformation", + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.GifToVideo => + "gif-to-video", + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationType.VideoThumbnail => + "video-thumbnail", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Configuration options for video transformations. +/// +[JsonConverter( + typeof(JsonModelConverter< + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFromRaw + >) +)] +public sealed record class VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions + : JsonModel +{ + /// + /// Audio codec used for encoding (aac or opus). + /// + public ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec + >? AudioCodec + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec + > + >("audio_codec"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("audio_codec", value); + } + } + + /// + /// Whether to automatically rotate the video based on metadata. + /// + public bool? AutoRotate + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("auto_rotate"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("auto_rotate", value); + } + } + + /// + /// Output format for the transformed video or thumbnail. + /// + public ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat + >? Format + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat + > + >("format"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("format", value); + } + } + + /// + /// Quality setting for the output video. + /// + public long? Quality + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("quality"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("quality", value); + } + } + + /// + /// Streaming protocol for adaptive bitrate streaming. + /// + public ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol + >? StreamProtocol + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol + > + >("stream_protocol"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("stream_protocol", value); + } + } + + /// + /// Array of quality representations for adaptive bitrate streaming. + /// + public IReadOnlyList? Variants + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("variants"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "variants", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Video codec used for encoding (h264, vp9, or av1). + /// + public ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec + >? VideoCodec + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum< + string, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec + > + >("video_codec"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("video_codec", value); + } + } + + /// + public override void Validate() + { + this.AudioCodec?.Validate(); + _ = this.AutoRotate; + this.Format?.Validate(); + _ = this.Quality; + this.StreamProtocol?.Validate(); + _ = this.Variants; + this.VideoCodec?.Validate(); + } + + public VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions( + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions videoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions + ) + : base(videoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions) + { } +#pragma warning restore CS8618 + + public VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFromRaw + : IFromRawJson +{ + /// + public VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptions.FromRawUnchecked( + rawData + ); +} + +/// +/// Audio codec used for encoding (aac or opus). +/// +[JsonConverter( + typeof(VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodecConverter) +)] +public enum VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec +{ + Aac, + Opus, +} + +sealed class VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodecConverter + : JsonConverter +{ + public override VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "aac" => + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac, + "opus" => + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Opus, + _ => + (VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec)( + -1 + ), + }; + } + + public override void Write( + Utf8JsonWriter writer, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Aac => + "aac", + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsAudioCodec.Opus => + "opus", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Output format for the transformed video or thumbnail. +/// +[JsonConverter( + typeof(VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormatConverter) +)] +public enum VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat +{ + Mp4, + Webm, + Jpg, + Png, + Webp, +} + +sealed class VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormatConverter + : JsonConverter +{ + public override VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "mp4" => + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4, + "webm" => + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Webm, + "jpg" => + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Jpg, + "png" => + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Png, + "webp" => + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Webp, + _ => + (VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat)( + -1 + ), + }; + } + + public override void Write( + Utf8JsonWriter writer, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Mp4 => + "mp4", + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Webm => + "webm", + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Jpg => + "jpg", + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Png => + "png", + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsFormat.Webp => + "webp", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Streaming protocol for adaptive bitrate streaming. +/// +[JsonConverter( + typeof(VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocolConverter) +)] +public enum VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol +{ + Hls, + Dash, +} + +sealed class VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocolConverter + : JsonConverter +{ + public override VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "HLS" => + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls, + "DASH" => + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Dash, + _ => + (VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol)( + -1 + ), + }; + } + + public override void Write( + Utf8JsonWriter writer, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Hls => + "HLS", + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsStreamProtocol.Dash => + "DASH", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Video codec used for encoding (h264, vp9, or av1). +/// +[JsonConverter( + typeof(VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodecConverter) +)] +public enum VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec +{ + H264, + Vp9, + Av1, +} + +sealed class VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodecConverter + : JsonConverter +{ + public override VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "h264" => + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264, + "vp9" => + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.Vp9, + "av1" => + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.Av1, + _ => + (VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec)( + -1 + ), + }; + } + + public override void Write( + Utf8JsonWriter writer, + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.H264 => + "h264", + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.Vp9 => + "vp9", + VideoTransformationReadyEventVideoTransformationReadyEventDataTransformationOptionsVideoCodec.Av1 => + "av1", + _ => throw new ImageKitInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Information about the transformed output video. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Output : JsonModel +{ + /// + /// URL to access the transformed video. + /// + public required string Url + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("url"); + } + init { this._rawData.Set("url", value); } + } + + /// + /// Metadata of the output video file. + /// + public VideoMetadata? VideoMetadata + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("video_metadata"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("video_metadata", value); + } + } + + /// + public override void Validate() + { + _ = this.Url; + this.VideoMetadata?.Validate(); + } + + public Output() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Output(Output output) + : base(output) { } +#pragma warning restore CS8618 + + public Output(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Output(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Output FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public Output(string url) + : this() + { + this.Url = url; + } +} + +class OutputFromRaw : IFromRawJson +{ + /// + public Output FromRawUnchecked(IReadOnlyDictionary rawData) => + Output.FromRawUnchecked(rawData); +} + +/// +/// Metadata of the output video file. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class VideoMetadata : JsonModel +{ + /// + /// Bitrate of the output video in bits per second. + /// + public required long Bitrate + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("bitrate"); + } + init { this._rawData.Set("bitrate", value); } + } + + /// + /// Duration of the output video in seconds. + /// + public required double Duration + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("duration"); + } + init { this._rawData.Set("duration", value); } + } + + /// + /// Height of the output video in pixels. + /// + public required long Height + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("height"); + } + init { this._rawData.Set("height", value); } + } + + /// + /// Width of the output video in pixels. + /// + public required long Width + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("width"); + } + init { this._rawData.Set("width", value); } + } + + /// + public override void Validate() + { + _ = this.Bitrate; + _ = this.Duration; + _ = this.Height; + _ = this.Width; + } + + public VideoMetadata() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoMetadata(VideoMetadata videoMetadata) + : base(videoMetadata) { } +#pragma warning restore CS8618 + + public VideoMetadata(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoMetadata(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoMetadata FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class VideoMetadataFromRaw : IFromRawJson +{ + /// + public VideoMetadata FromRawUnchecked(IReadOnlyDictionary rawData) => + VideoMetadata.FromRawUnchecked(rawData); +} + +/// +/// Information about the original request that triggered the video transformation. +/// +[JsonConverter( + typeof(JsonModelConverter< + VideoTransformationReadyEventVideoTransformationReadyEventRequest, + VideoTransformationReadyEventVideoTransformationReadyEventRequestFromRaw + >) +)] +public sealed record class VideoTransformationReadyEventVideoTransformationReadyEventRequest + : JsonModel +{ + /// + /// Full URL of the transformation request that was submitted. + /// + public required string Url + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("url"); + } + init { this._rawData.Set("url", value); } + } + + /// + /// Unique identifier for the originating transformation request. + /// + public required string XRequestID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("x_request_id"); + } + init { this._rawData.Set("x_request_id", value); } + } + + /// + /// User-Agent header from the original request that triggered the transformation. + /// + public string? UserAgent + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("user_agent"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("user_agent", value); + } + } + + /// + public override void Validate() + { + _ = this.Url; + _ = this.XRequestID; + _ = this.UserAgent; + } + + public VideoTransformationReadyEventVideoTransformationReadyEventRequest() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VideoTransformationReadyEventVideoTransformationReadyEventRequest( + VideoTransformationReadyEventVideoTransformationReadyEventRequest videoTransformationReadyEventVideoTransformationReadyEventRequest + ) + : base(videoTransformationReadyEventVideoTransformationReadyEventRequest) { } +#pragma warning restore CS8618 + + public VideoTransformationReadyEventVideoTransformationReadyEventRequest( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VideoTransformationReadyEventVideoTransformationReadyEventRequest( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VideoTransformationReadyEventVideoTransformationReadyEventRequest FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class VideoTransformationReadyEventVideoTransformationReadyEventRequestFromRaw + : IFromRawJson +{ + /// + public VideoTransformationReadyEventVideoTransformationReadyEventRequest FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + VideoTransformationReadyEventVideoTransformationReadyEventRequest.FromRawUnchecked(rawData); +} + +/// +/// Performance metrics for the transformation process. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Timings : JsonModel +{ + /// + /// Time spent downloading the source video from your origin or media library, + /// in milliseconds. + /// + public long? DownloadDuration + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("download_duration"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("download_duration", value); + } + } + + /// + /// Time spent encoding the video, in milliseconds. + /// + public long? EncodingDuration + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("encoding_duration"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("encoding_duration", value); + } + } + + /// + public override void Validate() + { + _ = this.DownloadDuration; + _ = this.EncodingDuration; + } + + public Timings() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Timings(Timings timings) + : base(timings) { } +#pragma warning restore CS8618 + + public Timings(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Timings(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Timings FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class TimingsFromRaw : IFromRawJson +{ + /// + public Timings FromRawUnchecked(IReadOnlyDictionary rawData) => + Timings.FromRawUnchecked(rawData); +} diff --git a/src/Imagekit/Services/AccountService.cs b/src/Imagekit/Services/AccountService.cs new file mode 100644 index 00000000..cc3702ac --- /dev/null +++ b/src/Imagekit/Services/AccountService.cs @@ -0,0 +1,92 @@ +using System; +using Imagekit.Core; +using Imagekit.Services.Accounts; + +namespace Imagekit.Services; + +/// +public sealed class AccountService : IAccountService +{ + readonly Lazy _withRawResponse; + + /// + public IAccountServiceWithRawResponse WithRawResponse + { + get { return _withRawResponse.Value; } + } + + readonly IImageKitClient _client; + + /// + public IAccountService WithOptions(Func modifier) + { + return new AccountService(this._client.WithOptions(modifier)); + } + + public AccountService(IImageKitClient client) + { + _client = client; + + _withRawResponse = new(() => new AccountServiceWithRawResponse(client.WithRawResponse)); + _usage = new(() => new UsageService(client)); + _origins = new(() => new OriginService(client)); + _urlEndpoints = new(() => new UrlEndpointService(client)); + } + + readonly Lazy _usage; + public IUsageService Usage + { + get { return _usage.Value; } + } + + readonly Lazy _origins; + public IOriginService Origins + { + get { return _origins.Value; } + } + + readonly Lazy _urlEndpoints; + public IUrlEndpointService UrlEndpoints + { + get { return _urlEndpoints.Value; } + } +} + +/// +public sealed class AccountServiceWithRawResponse : IAccountServiceWithRawResponse +{ + readonly IImageKitClientWithRawResponse _client; + + /// + public IAccountServiceWithRawResponse WithOptions(Func modifier) + { + return new AccountServiceWithRawResponse(this._client.WithOptions(modifier)); + } + + public AccountServiceWithRawResponse(IImageKitClientWithRawResponse client) + { + _client = client; + + _usage = new(() => new UsageServiceWithRawResponse(client)); + _origins = new(() => new OriginServiceWithRawResponse(client)); + _urlEndpoints = new(() => new UrlEndpointServiceWithRawResponse(client)); + } + + readonly Lazy _usage; + public IUsageServiceWithRawResponse Usage + { + get { return _usage.Value; } + } + + readonly Lazy _origins; + public IOriginServiceWithRawResponse Origins + { + get { return _origins.Value; } + } + + readonly Lazy _urlEndpoints; + public IUrlEndpointServiceWithRawResponse UrlEndpoints + { + get { return _urlEndpoints.Value; } + } +} diff --git a/src/Imagekit/Services/Accounts/IOriginService.cs b/src/Imagekit/Services/Accounts/IOriginService.cs new file mode 100644 index 00000000..0e1a156e --- /dev/null +++ b/src/Imagekit/Services/Accounts/IOriginService.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Models.Accounts.Origins; + +namespace Imagekit.Services.Accounts; + +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with breaking +/// changes in non-major versions. We may add new methods in the future that cause +/// existing derived classes to break. +/// +public interface IOriginService +{ + /// + /// Returns a view of this service that provides access to raw HTTP responses + /// for each method. + /// + IOriginServiceWithRawResponse WithRawResponse { get; } + + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IOriginService WithOptions(Func modifier); + + /// + /// **Note:** This API is currently in beta. Creates a new origin and returns the + /// origin object. + /// + Task Create( + OriginCreateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// **Note:** This API is currently in beta. Updates the origin identified by `id` + /// and returns the updated origin object. + /// + Task Update( + OriginUpdateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task Update( + string id, + OriginUpdateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// **Note:** This API is currently in beta. Returns an array of all configured + /// origins for the current account. + /// + Task> List( + OriginListParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// **Note:** This API is currently in beta. Permanently removes the origin + /// identified by `id`. If the origin is in use by any URL‑endpoints, the API will + /// return an error. + /// + Task Delete(OriginDeleteParams parameters, CancellationToken cancellationToken = default); + + /// + Task Delete( + string id, + OriginDeleteParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// **Note:** This API is currently in beta. Retrieves the origin identified by + /// `id`. + /// + Task Get( + OriginGetParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task Get( + string id, + OriginGetParams? parameters = null, + CancellationToken cancellationToken = default + ); +} + +/// +/// A view of that provides access to raw +/// HTTP responses for each method. +/// +public interface IOriginServiceWithRawResponse +{ + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IOriginServiceWithRawResponse WithOptions(Func modifier); + + /// + /// Returns a raw HTTP response for post /v1/accounts/origins, but is otherwise the + /// same as . + /// + Task> Create( + OriginCreateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for put /v1/accounts/origins/{id}, but is otherwise the + /// same as . + /// + Task> Update( + OriginUpdateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task> Update( + string id, + OriginUpdateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for get /v1/accounts/origins, but is otherwise the + /// same as . + /// + Task>> List( + OriginListParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for delete /v1/accounts/origins/{id}, but is otherwise the + /// same as . + /// + Task Delete( + OriginDeleteParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task Delete( + string id, + OriginDeleteParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for get /v1/accounts/origins/{id}, but is otherwise the + /// same as . + /// + Task> Get( + OriginGetParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task> Get( + string id, + OriginGetParams? parameters = null, + CancellationToken cancellationToken = default + ); +} diff --git a/src/Imagekit/Services/Accounts/IUrlEndpointService.cs b/src/Imagekit/Services/Accounts/IUrlEndpointService.cs new file mode 100644 index 00000000..f18fdb2e --- /dev/null +++ b/src/Imagekit/Services/Accounts/IUrlEndpointService.cs @@ -0,0 +1,174 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Models.Accounts.UrlEndpoints; + +namespace Imagekit.Services.Accounts; + +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with breaking +/// changes in non-major versions. We may add new methods in the future that cause +/// existing derived classes to break. +/// +public interface IUrlEndpointService +{ + /// + /// Returns a view of this service that provides access to raw HTTP responses + /// for each method. + /// + IUrlEndpointServiceWithRawResponse WithRawResponse { get; } + + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IUrlEndpointService WithOptions(Func modifier); + + /// + /// **Note:** This API is currently in beta. Creates a new URL‑endpoint and + /// returns the resulting object. + /// + Task Create( + UrlEndpointCreateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// **Note:** This API is currently in beta. Updates the URL‑endpoint identified + /// by `id` and returns the updated object. + /// + Task Update( + UrlEndpointUpdateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task Update( + string id, + UrlEndpointUpdateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// **Note:** This API is currently in beta. Returns an array of all URL‑endpoints + /// configured including the default URL-endpoint generated by ImageKit during + /// account creation. + /// + Task> List( + UrlEndpointListParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// **Note:** This API is currently in beta. Deletes the URL‑endpoint identified + /// by `id`. You cannot delete the default URL‑endpoint created by ImageKit during + /// account creation. + /// + Task Delete(UrlEndpointDeleteParams parameters, CancellationToken cancellationToken = default); + + /// + Task Delete( + string id, + UrlEndpointDeleteParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// **Note:** This API is currently in beta. Retrieves the URL‑endpoint identified + /// by `id`. + /// + Task Get( + UrlEndpointGetParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task Get( + string id, + UrlEndpointGetParams? parameters = null, + CancellationToken cancellationToken = default + ); +} + +/// +/// A view of that provides access to raw +/// HTTP responses for each method. +/// +public interface IUrlEndpointServiceWithRawResponse +{ + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IUrlEndpointServiceWithRawResponse WithOptions(Func modifier); + + /// + /// Returns a raw HTTP response for post /v1/accounts/url-endpoints, but is otherwise the + /// same as . + /// + Task> Create( + UrlEndpointCreateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for put /v1/accounts/url-endpoints/{id}, but is otherwise the + /// same as . + /// + Task> Update( + UrlEndpointUpdateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task> Update( + string id, + UrlEndpointUpdateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for get /v1/accounts/url-endpoints, but is otherwise the + /// same as . + /// + Task>> List( + UrlEndpointListParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for delete /v1/accounts/url-endpoints/{id}, but is otherwise the + /// same as . + /// + Task Delete( + UrlEndpointDeleteParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task Delete( + string id, + UrlEndpointDeleteParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for get /v1/accounts/url-endpoints/{id}, but is otherwise the + /// same as . + /// + Task> Get( + UrlEndpointGetParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task> Get( + string id, + UrlEndpointGetParams? parameters = null, + CancellationToken cancellationToken = default + ); +} diff --git a/src/Imagekit/Services/Accounts/IUsageService.cs b/src/Imagekit/Services/Accounts/IUsageService.cs new file mode 100644 index 00000000..8cdd7ea1 --- /dev/null +++ b/src/Imagekit/Services/Accounts/IUsageService.cs @@ -0,0 +1,62 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Models.Accounts.Usage; + +namespace Imagekit.Services.Accounts; + +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with breaking +/// changes in non-major versions. We may add new methods in the future that cause +/// existing derived classes to break. +/// +public interface IUsageService +{ + /// + /// Returns a view of this service that provides access to raw HTTP responses + /// for each method. + /// + IUsageServiceWithRawResponse WithRawResponse { get; } + + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IUsageService WithOptions(Func modifier); + + /// + /// Get the account usage information between two dates. Note that the API response + /// includes data from the start date while excluding data from the end date. In + /// other words, the data covers the period starting from the specified start date + /// up to, but not including, the end date. + /// + Task Get( + UsageGetParams parameters, + CancellationToken cancellationToken = default + ); +} + +/// +/// A view of that provides access to raw +/// HTTP responses for each method. +/// +public interface IUsageServiceWithRawResponse +{ + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IUsageServiceWithRawResponse WithOptions(Func modifier); + + /// + /// Returns a raw HTTP response for get /v1/accounts/usage, but is otherwise the + /// same as . + /// + Task> Get( + UsageGetParams parameters, + CancellationToken cancellationToken = default + ); +} diff --git a/src/Imagekit/Services/Accounts/OriginService.cs b/src/Imagekit/Services/Accounts/OriginService.cs new file mode 100644 index 00000000..7fa88caa --- /dev/null +++ b/src/Imagekit/Services/Accounts/OriginService.cs @@ -0,0 +1,322 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Accounts.Origins; + +namespace Imagekit.Services.Accounts; + +/// +public sealed class OriginService : IOriginService +{ + readonly Lazy _withRawResponse; + + /// + public IOriginServiceWithRawResponse WithRawResponse + { + get { return _withRawResponse.Value; } + } + + readonly IImageKitClient _client; + + /// + public IOriginService WithOptions(Func modifier) + { + return new OriginService(this._client.WithOptions(modifier)); + } + + public OriginService(IImageKitClient client) + { + _client = client; + + _withRawResponse = new(() => new OriginServiceWithRawResponse(client.WithRawResponse)); + } + + /// + public async Task Create( + OriginCreateParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Create(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public async Task Update( + OriginUpdateParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Update(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public Task Update( + string id, + OriginUpdateParams parameters, + CancellationToken cancellationToken = default + ) + { + return this.Update(parameters with { ID = id }, cancellationToken); + } + + /// + public async Task> List( + OriginListParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.List(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public Task Delete(OriginDeleteParams parameters, CancellationToken cancellationToken = default) + { + return this.WithRawResponse.Delete(parameters, cancellationToken); + } + + /// + public async Task Delete( + string id, + OriginDeleteParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + await this.Delete(parameters with { ID = id }, cancellationToken).ConfigureAwait(false); + } + + /// + public async Task Get( + OriginGetParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Get(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public Task Get( + string id, + OriginGetParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Get(parameters with { ID = id }, cancellationToken); + } +} + +/// +public sealed class OriginServiceWithRawResponse : IOriginServiceWithRawResponse +{ + readonly IImageKitClientWithRawResponse _client; + + /// + public IOriginServiceWithRawResponse WithOptions(Func modifier) + { + return new OriginServiceWithRawResponse(this._client.WithOptions(modifier)); + } + + public OriginServiceWithRawResponse(IImageKitClientWithRawResponse client) + { + _client = client; + } + + /// + public async Task> Create( + OriginCreateParams parameters, + CancellationToken cancellationToken = default + ) + { + HttpRequest request = new() + { + Method = HttpMethod.Post, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var originResponse = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + originResponse.Validate(); + } + return originResponse; + } + ); + } + + /// + public async Task> Update( + OriginUpdateParams parameters, + CancellationToken cancellationToken = default + ) + { + if (parameters.ID == null) + { + throw new ImageKitInvalidDataException("'parameters.ID' cannot be null"); + } + + HttpRequest request = new() + { + Method = HttpMethod.Put, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var originResponse = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + originResponse.Validate(); + } + return originResponse; + } + ); + } + + /// + public Task> Update( + string id, + OriginUpdateParams parameters, + CancellationToken cancellationToken = default + ) + { + return this.Update(parameters with { ID = id }, cancellationToken); + } + + /// + public async Task>> List( + OriginListParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + HttpRequest request = new() + { + Method = HttpMethod.Get, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var originResponses = await response + .Deserialize>(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + foreach (var item in originResponses) + { + item.Validate(); + } + } + return originResponses; + } + ); + } + + /// + public Task Delete( + OriginDeleteParams parameters, + CancellationToken cancellationToken = default + ) + { + if (parameters.ID == null) + { + throw new ImageKitInvalidDataException("'parameters.ID' cannot be null"); + } + + HttpRequest request = new() + { + Method = HttpMethod.Delete, + Params = parameters, + }; + return this._client.Execute(request, cancellationToken); + } + + /// + public Task Delete( + string id, + OriginDeleteParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Delete(parameters with { ID = id }, cancellationToken); + } + + /// + public async Task> Get( + OriginGetParams parameters, + CancellationToken cancellationToken = default + ) + { + if (parameters.ID == null) + { + throw new ImageKitInvalidDataException("'parameters.ID' cannot be null"); + } + + HttpRequest request = new() + { + Method = HttpMethod.Get, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var originResponse = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + originResponse.Validate(); + } + return originResponse; + } + ); + } + + /// + public Task> Get( + string id, + OriginGetParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Get(parameters with { ID = id }, cancellationToken); + } +} diff --git a/src/Imagekit/Services/Accounts/UrlEndpointService.cs b/src/Imagekit/Services/Accounts/UrlEndpointService.cs new file mode 100644 index 00000000..eac81713 --- /dev/null +++ b/src/Imagekit/Services/Accounts/UrlEndpointService.cs @@ -0,0 +1,327 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Accounts.UrlEndpoints; + +namespace Imagekit.Services.Accounts; + +/// +public sealed class UrlEndpointService : IUrlEndpointService +{ + readonly Lazy _withRawResponse; + + /// + public IUrlEndpointServiceWithRawResponse WithRawResponse + { + get { return _withRawResponse.Value; } + } + + readonly IImageKitClient _client; + + /// + public IUrlEndpointService WithOptions(Func modifier) + { + return new UrlEndpointService(this._client.WithOptions(modifier)); + } + + public UrlEndpointService(IImageKitClient client) + { + _client = client; + + _withRawResponse = new(() => new UrlEndpointServiceWithRawResponse(client.WithRawResponse)); + } + + /// + public async Task Create( + UrlEndpointCreateParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Create(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public async Task Update( + UrlEndpointUpdateParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Update(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public Task Update( + string id, + UrlEndpointUpdateParams parameters, + CancellationToken cancellationToken = default + ) + { + return this.Update(parameters with { ID = id }, cancellationToken); + } + + /// + public async Task> List( + UrlEndpointListParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.List(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public Task Delete( + UrlEndpointDeleteParams parameters, + CancellationToken cancellationToken = default + ) + { + return this.WithRawResponse.Delete(parameters, cancellationToken); + } + + /// + public async Task Delete( + string id, + UrlEndpointDeleteParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + await this.Delete(parameters with { ID = id }, cancellationToken).ConfigureAwait(false); + } + + /// + public async Task Get( + UrlEndpointGetParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Get(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public Task Get( + string id, + UrlEndpointGetParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Get(parameters with { ID = id }, cancellationToken); + } +} + +/// +public sealed class UrlEndpointServiceWithRawResponse : IUrlEndpointServiceWithRawResponse +{ + readonly IImageKitClientWithRawResponse _client; + + /// + public IUrlEndpointServiceWithRawResponse WithOptions( + Func modifier + ) + { + return new UrlEndpointServiceWithRawResponse(this._client.WithOptions(modifier)); + } + + public UrlEndpointServiceWithRawResponse(IImageKitClientWithRawResponse client) + { + _client = client; + } + + /// + public async Task> Create( + UrlEndpointCreateParams parameters, + CancellationToken cancellationToken = default + ) + { + HttpRequest request = new() + { + Method = HttpMethod.Post, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var urlEndpointResponse = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + urlEndpointResponse.Validate(); + } + return urlEndpointResponse; + } + ); + } + + /// + public async Task> Update( + UrlEndpointUpdateParams parameters, + CancellationToken cancellationToken = default + ) + { + if (parameters.ID == null) + { + throw new ImageKitInvalidDataException("'parameters.ID' cannot be null"); + } + + HttpRequest request = new() + { + Method = HttpMethod.Put, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var urlEndpointResponse = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + urlEndpointResponse.Validate(); + } + return urlEndpointResponse; + } + ); + } + + /// + public Task> Update( + string id, + UrlEndpointUpdateParams parameters, + CancellationToken cancellationToken = default + ) + { + return this.Update(parameters with { ID = id }, cancellationToken); + } + + /// + public async Task>> List( + UrlEndpointListParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + HttpRequest request = new() + { + Method = HttpMethod.Get, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var urlEndpointResponses = await response + .Deserialize>(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + foreach (var item in urlEndpointResponses) + { + item.Validate(); + } + } + return urlEndpointResponses; + } + ); + } + + /// + public Task Delete( + UrlEndpointDeleteParams parameters, + CancellationToken cancellationToken = default + ) + { + if (parameters.ID == null) + { + throw new ImageKitInvalidDataException("'parameters.ID' cannot be null"); + } + + HttpRequest request = new() + { + Method = HttpMethod.Delete, + Params = parameters, + }; + return this._client.Execute(request, cancellationToken); + } + + /// + public Task Delete( + string id, + UrlEndpointDeleteParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Delete(parameters with { ID = id }, cancellationToken); + } + + /// + public async Task> Get( + UrlEndpointGetParams parameters, + CancellationToken cancellationToken = default + ) + { + if (parameters.ID == null) + { + throw new ImageKitInvalidDataException("'parameters.ID' cannot be null"); + } + + HttpRequest request = new() + { + Method = HttpMethod.Get, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var urlEndpointResponse = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + urlEndpointResponse.Validate(); + } + return urlEndpointResponse; + } + ); + } + + /// + public Task> Get( + string id, + UrlEndpointGetParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Get(parameters with { ID = id }, cancellationToken); + } +} diff --git a/src/Imagekit/Services/Accounts/UsageService.cs b/src/Imagekit/Services/Accounts/UsageService.cs new file mode 100644 index 00000000..1f996022 --- /dev/null +++ b/src/Imagekit/Services/Accounts/UsageService.cs @@ -0,0 +1,92 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Models.Accounts.Usage; + +namespace Imagekit.Services.Accounts; + +/// +public sealed class UsageService : IUsageService +{ + readonly Lazy _withRawResponse; + + /// + public IUsageServiceWithRawResponse WithRawResponse + { + get { return _withRawResponse.Value; } + } + + readonly IImageKitClient _client; + + /// + public IUsageService WithOptions(Func modifier) + { + return new UsageService(this._client.WithOptions(modifier)); + } + + public UsageService(IImageKitClient client) + { + _client = client; + + _withRawResponse = new(() => new UsageServiceWithRawResponse(client.WithRawResponse)); + } + + /// + public async Task Get( + UsageGetParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Get(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } +} + +/// +public sealed class UsageServiceWithRawResponse : IUsageServiceWithRawResponse +{ + readonly IImageKitClientWithRawResponse _client; + + /// + public IUsageServiceWithRawResponse WithOptions(Func modifier) + { + return new UsageServiceWithRawResponse(this._client.WithOptions(modifier)); + } + + public UsageServiceWithRawResponse(IImageKitClientWithRawResponse client) + { + _client = client; + } + + /// + public async Task> Get( + UsageGetParams parameters, + CancellationToken cancellationToken = default + ) + { + HttpRequest request = new() + { + Method = HttpMethod.Get, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var usage = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + usage.Validate(); + } + return usage; + } + ); + } +} diff --git a/src/Imagekit/Services/AssetService.cs b/src/Imagekit/Services/AssetService.cs new file mode 100644 index 00000000..bc4c31f8 --- /dev/null +++ b/src/Imagekit/Services/AssetService.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Models.Assets; + +namespace Imagekit.Services; + +/// +public sealed class AssetService : IAssetService +{ + readonly Lazy _withRawResponse; + + /// + public IAssetServiceWithRawResponse WithRawResponse + { + get { return _withRawResponse.Value; } + } + + readonly IImageKitClient _client; + + /// + public IAssetService WithOptions(Func modifier) + { + return new AssetService(this._client.WithOptions(modifier)); + } + + public AssetService(IImageKitClient client) + { + _client = client; + + _withRawResponse = new(() => new AssetServiceWithRawResponse(client.WithRawResponse)); + } + + /// + public async Task> List( + AssetListParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.List(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } +} + +/// +public sealed class AssetServiceWithRawResponse : IAssetServiceWithRawResponse +{ + readonly IImageKitClientWithRawResponse _client; + + /// + public IAssetServiceWithRawResponse WithOptions(Func modifier) + { + return new AssetServiceWithRawResponse(this._client.WithOptions(modifier)); + } + + public AssetServiceWithRawResponse(IImageKitClientWithRawResponse client) + { + _client = client; + } + + /// + public async Task>> List( + AssetListParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + HttpRequest request = new() + { + Method = HttpMethod.Get, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var assets = await response + .Deserialize>(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + foreach (var item in assets) + { + item.Validate(); + } + } + return assets; + } + ); + } +} diff --git a/src/Imagekit/Services/Beta/IV2Service.cs b/src/Imagekit/Services/Beta/IV2Service.cs new file mode 100644 index 00000000..5dae1527 --- /dev/null +++ b/src/Imagekit/Services/Beta/IV2Service.cs @@ -0,0 +1,44 @@ +using System; +using Imagekit.Core; +using V2 = Imagekit.Services.Beta.V2; + +namespace Imagekit.Services.Beta; + +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with breaking +/// changes in non-major versions. We may add new methods in the future that cause +/// existing derived classes to break. +/// +public interface IV2Service +{ + /// + /// Returns a view of this service that provides access to raw HTTP responses + /// for each method. + /// + IV2ServiceWithRawResponse WithRawResponse { get; } + + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IV2Service WithOptions(Func modifier); + + V2::IFileService Files { get; } +} + +/// +/// A view of that provides access to raw +/// HTTP responses for each method. +/// +public interface IV2ServiceWithRawResponse +{ + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IV2ServiceWithRawResponse WithOptions(Func modifier); + + V2::IFileServiceWithRawResponse Files { get; } +} diff --git a/src/Imagekit/Services/Beta/V2/FileService.cs b/src/Imagekit/Services/Beta/V2/FileService.cs new file mode 100644 index 00000000..a683e530 --- /dev/null +++ b/src/Imagekit/Services/Beta/V2/FileService.cs @@ -0,0 +1,92 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Models.Beta.V2.Files; + +namespace Imagekit.Services.Beta.V2; + +/// +public sealed class FileService : IFileService +{ + readonly Lazy _withRawResponse; + + /// + public IFileServiceWithRawResponse WithRawResponse + { + get { return _withRawResponse.Value; } + } + + readonly IImageKitClient _client; + + /// + public IFileService WithOptions(Func modifier) + { + return new FileService(this._client.WithOptions(modifier)); + } + + public FileService(IImageKitClient client) + { + _client = client; + + _withRawResponse = new(() => new FileServiceWithRawResponse(client.WithRawResponse)); + } + + /// + public async Task Upload( + FileUploadParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Upload(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } +} + +/// +public sealed class FileServiceWithRawResponse : IFileServiceWithRawResponse +{ + readonly IImageKitClientWithRawResponse _client; + + /// + public IFileServiceWithRawResponse WithOptions(Func modifier) + { + return new FileServiceWithRawResponse(this._client.WithOptions(modifier)); + } + + public FileServiceWithRawResponse(IImageKitClientWithRawResponse client) + { + _client = client; + } + + /// + public async Task> Upload( + FileUploadParams parameters, + CancellationToken cancellationToken = default + ) + { + HttpRequest request = new() + { + Method = HttpMethod.Post, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var deserializedResponse = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + deserializedResponse.Validate(); + } + return deserializedResponse; + } + ); + } +} diff --git a/src/Imagekit/Services/Beta/V2/IFileService.cs b/src/Imagekit/Services/Beta/V2/IFileService.cs new file mode 100644 index 00000000..07d40cce --- /dev/null +++ b/src/Imagekit/Services/Beta/V2/IFileService.cs @@ -0,0 +1,84 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Models.Beta.V2.Files; + +namespace Imagekit.Services.Beta.V2; + +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with breaking +/// changes in non-major versions. We may add new methods in the future that cause +/// existing derived classes to break. +/// +public interface IFileService +{ + /// + /// Returns a view of this service that provides access to raw HTTP responses + /// for each method. + /// + IFileServiceWithRawResponse WithRawResponse { get; } + + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IFileService WithOptions(Func modifier); + + /// + /// The V2 API enhances security by verifying the entire payload using JWT. This API + /// is in beta. + /// + /// ImageKit.io allows you to upload files directly from both the server and + /// client sides. For server-side uploads, private API key authentication is used. + /// For client-side uploads, generate a one-time `token` from your secure backend + /// using private API. [Learn + /// more](/docs/api-reference/upload-file/upload-file-v2#how-to-implement-secure-client-side-file-upload) + /// about how to implement secure client-side file upload. + /// + /// **File size limit** \ On the free plan, the maximum upload file sizes are + /// 25MB for images, audio, and raw files, and 100MB for videos. On the Lite paid + /// plan, these limits increase to 40MB for images, audio, and raw files and 300MB + /// for videos, whereas on the Pro paid plan, these limits increase to 50MB for + /// images, audio, and raw files and 2GB for videos. These limits can be further + /// increased with enterprise plans. + /// + /// **Version limit** \ A file can have a maximum of 100 versions. + /// + /// **Demo applications** + /// + /// - A full-fledged [upload widget using + /// Uppy](https://github.com/imagekit-samples/uppy-uploader), supporting file + /// selections from local storage, URL, Dropbox, Google Drive, Instagram, and more. + /// - [Quick start guides](/docs/quick-start-guides) for various frameworks and + /// technologies. + /// + Task Upload( + FileUploadParams parameters, + CancellationToken cancellationToken = default + ); +} + +/// +/// A view of that provides access to raw +/// HTTP responses for each method. +/// +public interface IFileServiceWithRawResponse +{ + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IFileServiceWithRawResponse WithOptions(Func modifier); + + /// + /// Returns a raw HTTP response for post /api/v2/files/upload, but is otherwise the + /// same as . + /// + Task> Upload( + FileUploadParams parameters, + CancellationToken cancellationToken = default + ); +} diff --git a/src/Imagekit/Services/Beta/V2Service.cs b/src/Imagekit/Services/Beta/V2Service.cs new file mode 100644 index 00000000..ae59d124 --- /dev/null +++ b/src/Imagekit/Services/Beta/V2Service.cs @@ -0,0 +1,64 @@ +using System; +using Imagekit.Core; +using V2 = Imagekit.Services.Beta.V2; + +namespace Imagekit.Services.Beta; + +/// +public sealed class V2Service : IV2Service +{ + readonly Lazy _withRawResponse; + + /// + public IV2ServiceWithRawResponse WithRawResponse + { + get { return _withRawResponse.Value; } + } + + readonly IImageKitClient _client; + + /// + public IV2Service WithOptions(Func modifier) + { + return new V2Service(this._client.WithOptions(modifier)); + } + + public V2Service(IImageKitClient client) + { + _client = client; + + _withRawResponse = new(() => new V2ServiceWithRawResponse(client.WithRawResponse)); + _files = new(() => new V2::FileService(client)); + } + + readonly Lazy _files; + public V2::IFileService Files + { + get { return _files.Value; } + } +} + +/// +public sealed class V2ServiceWithRawResponse : IV2ServiceWithRawResponse +{ + readonly IImageKitClientWithRawResponse _client; + + /// + public IV2ServiceWithRawResponse WithOptions(Func modifier) + { + return new V2ServiceWithRawResponse(this._client.WithOptions(modifier)); + } + + public V2ServiceWithRawResponse(IImageKitClientWithRawResponse client) + { + _client = client; + + _files = new(() => new V2::FileServiceWithRawResponse(client)); + } + + readonly Lazy _files; + public V2::IFileServiceWithRawResponse Files + { + get { return _files.Value; } + } +} diff --git a/src/Imagekit/Services/BetaService.cs b/src/Imagekit/Services/BetaService.cs new file mode 100644 index 00000000..fe5ee4dc --- /dev/null +++ b/src/Imagekit/Services/BetaService.cs @@ -0,0 +1,64 @@ +using System; +using Imagekit.Core; +using Imagekit.Services.Beta; + +namespace Imagekit.Services; + +/// +public sealed class BetaService : IBetaService +{ + readonly Lazy _withRawResponse; + + /// + public IBetaServiceWithRawResponse WithRawResponse + { + get { return _withRawResponse.Value; } + } + + readonly IImageKitClient _client; + + /// + public IBetaService WithOptions(Func modifier) + { + return new BetaService(this._client.WithOptions(modifier)); + } + + public BetaService(IImageKitClient client) + { + _client = client; + + _withRawResponse = new(() => new BetaServiceWithRawResponse(client.WithRawResponse)); + _v2 = new(() => new V2Service(client)); + } + + readonly Lazy _v2; + public IV2Service V2 + { + get { return _v2.Value; } + } +} + +/// +public sealed class BetaServiceWithRawResponse : IBetaServiceWithRawResponse +{ + readonly IImageKitClientWithRawResponse _client; + + /// + public IBetaServiceWithRawResponse WithOptions(Func modifier) + { + return new BetaServiceWithRawResponse(this._client.WithOptions(modifier)); + } + + public BetaServiceWithRawResponse(IImageKitClientWithRawResponse client) + { + _client = client; + + _v2 = new(() => new V2ServiceWithRawResponse(client)); + } + + readonly Lazy _v2; + public IV2ServiceWithRawResponse V2 + { + get { return _v2.Value; } + } +} diff --git a/src/Imagekit/Services/Cache/IInvalidationService.cs b/src/Imagekit/Services/Cache/IInvalidationService.cs new file mode 100644 index 00000000..7bfc13d1 --- /dev/null +++ b/src/Imagekit/Services/Cache/IInvalidationService.cs @@ -0,0 +1,92 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Models.Cache.Invalidation; + +namespace Imagekit.Services.Cache; + +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with breaking +/// changes in non-major versions. We may add new methods in the future that cause +/// existing derived classes to break. +/// +public interface IInvalidationService +{ + /// + /// Returns a view of this service that provides access to raw HTTP responses + /// for each method. + /// + IInvalidationServiceWithRawResponse WithRawResponse { get; } + + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IInvalidationService WithOptions(Func modifier); + + /// + /// This API will purge CDN cache and ImageKit.io's internal cache for a file. Note: + /// Purge cache is an asynchronous process and it may take some time to reflect the changes. + /// + /// + Task Create( + InvalidationCreateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// This API returns the status of a purge cache request. + /// + Task Get( + InvalidationGetParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task Get( + string requestID, + InvalidationGetParams? parameters = null, + CancellationToken cancellationToken = default + ); +} + +/// +/// A view of that provides access to raw +/// HTTP responses for each method. +/// +public interface IInvalidationServiceWithRawResponse +{ + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IInvalidationServiceWithRawResponse WithOptions(Func modifier); + + /// + /// Returns a raw HTTP response for post /v1/files/purge, but is otherwise the + /// same as . + /// + Task> Create( + InvalidationCreateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for get /v1/files/purge/{requestId}, but is otherwise the + /// same as . + /// + Task> Get( + InvalidationGetParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task> Get( + string requestID, + InvalidationGetParams? parameters = null, + CancellationToken cancellationToken = default + ); +} diff --git a/src/Imagekit/Services/Cache/InvalidationService.cs b/src/Imagekit/Services/Cache/InvalidationService.cs new file mode 100644 index 00000000..13ecbe85 --- /dev/null +++ b/src/Imagekit/Services/Cache/InvalidationService.cs @@ -0,0 +1,166 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Cache.Invalidation; + +namespace Imagekit.Services.Cache; + +/// +public sealed class InvalidationService : IInvalidationService +{ + readonly Lazy _withRawResponse; + + /// + public IInvalidationServiceWithRawResponse WithRawResponse + { + get { return _withRawResponse.Value; } + } + + readonly IImageKitClient _client; + + /// + public IInvalidationService WithOptions(Func modifier) + { + return new InvalidationService(this._client.WithOptions(modifier)); + } + + public InvalidationService(IImageKitClient client) + { + _client = client; + + _withRawResponse = new(() => + new InvalidationServiceWithRawResponse(client.WithRawResponse) + ); + } + + /// + public async Task Create( + InvalidationCreateParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Create(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public async Task Get( + InvalidationGetParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Get(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public Task Get( + string requestID, + InvalidationGetParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Get(parameters with { RequestID = requestID }, cancellationToken); + } +} + +/// +public sealed class InvalidationServiceWithRawResponse : IInvalidationServiceWithRawResponse +{ + readonly IImageKitClientWithRawResponse _client; + + /// + public IInvalidationServiceWithRawResponse WithOptions( + Func modifier + ) + { + return new InvalidationServiceWithRawResponse(this._client.WithOptions(modifier)); + } + + public InvalidationServiceWithRawResponse(IImageKitClientWithRawResponse client) + { + _client = client; + } + + /// + public async Task> Create( + InvalidationCreateParams parameters, + CancellationToken cancellationToken = default + ) + { + HttpRequest request = new() + { + Method = HttpMethod.Post, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var invalidation = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + invalidation.Validate(); + } + return invalidation; + } + ); + } + + /// + public async Task> Get( + InvalidationGetParams parameters, + CancellationToken cancellationToken = default + ) + { + if (parameters.RequestID == null) + { + throw new ImageKitInvalidDataException("'parameters.RequestID' cannot be null"); + } + + HttpRequest request = new() + { + Method = HttpMethod.Get, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var invalidation = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + invalidation.Validate(); + } + return invalidation; + } + ); + } + + /// + public Task> Get( + string requestID, + InvalidationGetParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Get(parameters with { RequestID = requestID }, cancellationToken); + } +} diff --git a/src/Imagekit/Services/CacheService.cs b/src/Imagekit/Services/CacheService.cs new file mode 100644 index 00000000..d66caaeb --- /dev/null +++ b/src/Imagekit/Services/CacheService.cs @@ -0,0 +1,64 @@ +using System; +using Imagekit.Core; +using Imagekit.Services.Cache; + +namespace Imagekit.Services; + +/// +public sealed class CacheService : ICacheService +{ + readonly Lazy _withRawResponse; + + /// + public ICacheServiceWithRawResponse WithRawResponse + { + get { return _withRawResponse.Value; } + } + + readonly IImageKitClient _client; + + /// + public ICacheService WithOptions(Func modifier) + { + return new CacheService(this._client.WithOptions(modifier)); + } + + public CacheService(IImageKitClient client) + { + _client = client; + + _withRawResponse = new(() => new CacheServiceWithRawResponse(client.WithRawResponse)); + _invalidation = new(() => new InvalidationService(client)); + } + + readonly Lazy _invalidation; + public IInvalidationService Invalidation + { + get { return _invalidation.Value; } + } +} + +/// +public sealed class CacheServiceWithRawResponse : ICacheServiceWithRawResponse +{ + readonly IImageKitClientWithRawResponse _client; + + /// + public ICacheServiceWithRawResponse WithOptions(Func modifier) + { + return new CacheServiceWithRawResponse(this._client.WithOptions(modifier)); + } + + public CacheServiceWithRawResponse(IImageKitClientWithRawResponse client) + { + _client = client; + + _invalidation = new(() => new InvalidationServiceWithRawResponse(client)); + } + + readonly Lazy _invalidation; + public IInvalidationServiceWithRawResponse Invalidation + { + get { return _invalidation.Value; } + } +} diff --git a/src/Imagekit/Services/CustomMetadataFieldService.cs b/src/Imagekit/Services/CustomMetadataFieldService.cs new file mode 100644 index 00000000..b469196c --- /dev/null +++ b/src/Imagekit/Services/CustomMetadataFieldService.cs @@ -0,0 +1,282 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.CustomMetadataFields; + +namespace Imagekit.Services; + +/// +public sealed class CustomMetadataFieldService : ICustomMetadataFieldService +{ + readonly Lazy _withRawResponse; + + /// + public ICustomMetadataFieldServiceWithRawResponse WithRawResponse + { + get { return _withRawResponse.Value; } + } + + readonly IImageKitClient _client; + + /// + public ICustomMetadataFieldService WithOptions(Func modifier) + { + return new CustomMetadataFieldService(this._client.WithOptions(modifier)); + } + + public CustomMetadataFieldService(IImageKitClient client) + { + _client = client; + + _withRawResponse = new(() => + new CustomMetadataFieldServiceWithRawResponse(client.WithRawResponse) + ); + } + + /// + public async Task Create( + CustomMetadataFieldCreateParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Create(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public async Task Update( + CustomMetadataFieldUpdateParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Update(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public Task Update( + string id, + CustomMetadataFieldUpdateParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Update(parameters with { ID = id }, cancellationToken); + } + + /// + public async Task> List( + CustomMetadataFieldListParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.List(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public async Task Delete( + CustomMetadataFieldDeleteParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Delete(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public Task Delete( + string id, + CustomMetadataFieldDeleteParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Delete(parameters with { ID = id }, cancellationToken); + } +} + +/// +public sealed class CustomMetadataFieldServiceWithRawResponse + : ICustomMetadataFieldServiceWithRawResponse +{ + readonly IImageKitClientWithRawResponse _client; + + /// + public ICustomMetadataFieldServiceWithRawResponse WithOptions( + Func modifier + ) + { + return new CustomMetadataFieldServiceWithRawResponse(this._client.WithOptions(modifier)); + } + + public CustomMetadataFieldServiceWithRawResponse(IImageKitClientWithRawResponse client) + { + _client = client; + } + + /// + public async Task> Create( + CustomMetadataFieldCreateParams parameters, + CancellationToken cancellationToken = default + ) + { + HttpRequest request = new() + { + Method = HttpMethod.Post, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var customMetadataField = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + customMetadataField.Validate(); + } + return customMetadataField; + } + ); + } + + /// + public async Task> Update( + CustomMetadataFieldUpdateParams parameters, + CancellationToken cancellationToken = default + ) + { + if (parameters.ID == null) + { + throw new ImageKitInvalidDataException("'parameters.ID' cannot be null"); + } + + HttpRequest request = new() + { + Method = ImageKitClientWithRawResponse.PatchMethod, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var customMetadataField = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + customMetadataField.Validate(); + } + return customMetadataField; + } + ); + } + + /// + public Task> Update( + string id, + CustomMetadataFieldUpdateParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Update(parameters with { ID = id }, cancellationToken); + } + + /// + public async Task>> List( + CustomMetadataFieldListParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + HttpRequest request = new() + { + Method = HttpMethod.Get, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var customMetadataFields = await response + .Deserialize>(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + foreach (var item in customMetadataFields) + { + item.Validate(); + } + } + return customMetadataFields; + } + ); + } + + /// + public async Task> Delete( + CustomMetadataFieldDeleteParams parameters, + CancellationToken cancellationToken = default + ) + { + if (parameters.ID == null) + { + throw new ImageKitInvalidDataException("'parameters.ID' cannot be null"); + } + + HttpRequest request = new() + { + Method = HttpMethod.Delete, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var customMetadataField = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + customMetadataField.Validate(); + } + return customMetadataField; + } + ); + } + + /// + public Task> Delete( + string id, + CustomMetadataFieldDeleteParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Delete(parameters with { ID = id }, cancellationToken); + } +} diff --git a/src/Imagekit/Services/DummyService.cs b/src/Imagekit/Services/DummyService.cs new file mode 100644 index 00000000..26d40dec --- /dev/null +++ b/src/Imagekit/Services/DummyService.cs @@ -0,0 +1,77 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Models.Dummy; + +namespace Imagekit.Services; + +/// +public sealed class DummyService : IDummyService +{ + readonly Lazy _withRawResponse; + + /// + public IDummyServiceWithRawResponse WithRawResponse + { + get { return _withRawResponse.Value; } + } + + readonly IImageKitClient _client; + + /// + public IDummyService WithOptions(Func modifier) + { + return new DummyService(this._client.WithOptions(modifier)); + } + + public DummyService(IImageKitClient client) + { + _client = client; + + _withRawResponse = new(() => new DummyServiceWithRawResponse(client.WithRawResponse)); + } + + /// + public Task Create( + DummyCreateParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + return this.WithRawResponse.Create(parameters, cancellationToken); + } +} + +/// +public sealed class DummyServiceWithRawResponse : IDummyServiceWithRawResponse +{ + readonly IImageKitClientWithRawResponse _client; + + /// + public IDummyServiceWithRawResponse WithOptions(Func modifier) + { + return new DummyServiceWithRawResponse(this._client.WithOptions(modifier)); + } + + public DummyServiceWithRawResponse(IImageKitClientWithRawResponse client) + { + _client = client; + } + + /// + public Task Create( + DummyCreateParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + HttpRequest request = new() + { + Method = HttpMethod.Post, + Params = parameters, + }; + return this._client.Execute(request, cancellationToken); + } +} diff --git a/src/Imagekit/Services/FileService.cs b/src/Imagekit/Services/FileService.cs new file mode 100644 index 00000000..e90eac93 --- /dev/null +++ b/src/Imagekit/Services/FileService.cs @@ -0,0 +1,435 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Files; +using Imagekit.Services.Files; + +namespace Imagekit.Services; + +/// +public sealed class FileService : IFileService +{ + readonly Lazy _withRawResponse; + + /// + public IFileServiceWithRawResponse WithRawResponse + { + get { return _withRawResponse.Value; } + } + + readonly IImageKitClient _client; + + /// + public IFileService WithOptions(Func modifier) + { + return new FileService(this._client.WithOptions(modifier)); + } + + public FileService(IImageKitClient client) + { + _client = client; + + _withRawResponse = new(() => new FileServiceWithRawResponse(client.WithRawResponse)); + _bulk = new(() => new BulkService(client)); + _versions = new(() => new VersionService(client)); + _metadata = new(() => new MetadataService(client)); + } + + readonly Lazy _bulk; + public IBulkService Bulk + { + get { return _bulk.Value; } + } + + readonly Lazy _versions; + public IVersionService Versions + { + get { return _versions.Value; } + } + + readonly Lazy _metadata; + public IMetadataService Metadata + { + get { return _metadata.Value; } + } + + /// + public async Task Update( + FileUpdateParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Update(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public Task Update( + string fileID, + FileUpdateParams parameters, + CancellationToken cancellationToken = default + ) + { + return this.Update(parameters with { FileID = fileID }, cancellationToken); + } + + /// + public Task Delete(FileDeleteParams parameters, CancellationToken cancellationToken = default) + { + return this.WithRawResponse.Delete(parameters, cancellationToken); + } + + /// + public async Task Delete( + string fileID, + FileDeleteParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + await this.Delete(parameters with { FileID = fileID }, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task Copy( + FileCopyParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Copy(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public async Task Get( + FileGetParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Get(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public Task Get( + string fileID, + FileGetParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Get(parameters with { FileID = fileID }, cancellationToken); + } + + /// + public async Task Move( + FileMoveParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Move(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public async Task Rename( + FileRenameParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Rename(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public async Task Upload( + FileUploadParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Upload(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } +} + +/// +public sealed class FileServiceWithRawResponse : IFileServiceWithRawResponse +{ + readonly IImageKitClientWithRawResponse _client; + + /// + public IFileServiceWithRawResponse WithOptions(Func modifier) + { + return new FileServiceWithRawResponse(this._client.WithOptions(modifier)); + } + + public FileServiceWithRawResponse(IImageKitClientWithRawResponse client) + { + _client = client; + + _bulk = new(() => new BulkServiceWithRawResponse(client)); + _versions = new(() => new VersionServiceWithRawResponse(client)); + _metadata = new(() => new MetadataServiceWithRawResponse(client)); + } + + readonly Lazy _bulk; + public IBulkServiceWithRawResponse Bulk + { + get { return _bulk.Value; } + } + + readonly Lazy _versions; + public IVersionServiceWithRawResponse Versions + { + get { return _versions.Value; } + } + + readonly Lazy _metadata; + public IMetadataServiceWithRawResponse Metadata + { + get { return _metadata.Value; } + } + + /// + public async Task> Update( + FileUpdateParams parameters, + CancellationToken cancellationToken = default + ) + { + if (parameters.FileID == null) + { + throw new ImageKitInvalidDataException("'parameters.FileID' cannot be null"); + } + + HttpRequest request = new() + { + Method = ImageKitClientWithRawResponse.PatchMethod, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var file = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + file.Validate(); + } + return file; + } + ); + } + + /// + public Task> Update( + string fileID, + FileUpdateParams parameters, + CancellationToken cancellationToken = default + ) + { + return this.Update(parameters with { FileID = fileID }, cancellationToken); + } + + /// + public Task Delete( + FileDeleteParams parameters, + CancellationToken cancellationToken = default + ) + { + if (parameters.FileID == null) + { + throw new ImageKitInvalidDataException("'parameters.FileID' cannot be null"); + } + + HttpRequest request = new() + { + Method = HttpMethod.Delete, + Params = parameters, + }; + return this._client.Execute(request, cancellationToken); + } + + /// + public Task Delete( + string fileID, + FileDeleteParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Delete(parameters with { FileID = fileID }, cancellationToken); + } + + /// + public async Task> Copy( + FileCopyParams parameters, + CancellationToken cancellationToken = default + ) + { + HttpRequest request = new() + { + Method = HttpMethod.Post, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var deserializedResponse = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + deserializedResponse.Validate(); + } + return deserializedResponse; + } + ); + } + + /// + public async Task> Get( + FileGetParams parameters, + CancellationToken cancellationToken = default + ) + { + if (parameters.FileID == null) + { + throw new ImageKitInvalidDataException("'parameters.FileID' cannot be null"); + } + + HttpRequest request = new() { Method = HttpMethod.Get, Params = parameters }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var file = await response.Deserialize(token).ConfigureAwait(false); + if (this._client.ResponseValidation) + { + file.Validate(); + } + return file; + } + ); + } + + /// + public Task> Get( + string fileID, + FileGetParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Get(parameters with { FileID = fileID }, cancellationToken); + } + + /// + public async Task> Move( + FileMoveParams parameters, + CancellationToken cancellationToken = default + ) + { + HttpRequest request = new() + { + Method = HttpMethod.Post, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var deserializedResponse = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + deserializedResponse.Validate(); + } + return deserializedResponse; + } + ); + } + + /// + public async Task> Rename( + FileRenameParams parameters, + CancellationToken cancellationToken = default + ) + { + HttpRequest request = new() + { + Method = HttpMethod.Put, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var deserializedResponse = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + deserializedResponse.Validate(); + } + return deserializedResponse; + } + ); + } + + /// + public async Task> Upload( + FileUploadParams parameters, + CancellationToken cancellationToken = default + ) + { + HttpRequest request = new() + { + Method = HttpMethod.Post, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var deserializedResponse = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + deserializedResponse.Validate(); + } + return deserializedResponse; + } + ); + } +} diff --git a/src/Imagekit/Services/Files/BulkService.cs b/src/Imagekit/Services/Files/BulkService.cs new file mode 100644 index 00000000..288b2837 --- /dev/null +++ b/src/Imagekit/Services/Files/BulkService.cs @@ -0,0 +1,212 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Models.Files.Bulk; + +namespace Imagekit.Services.Files; + +/// +public sealed class BulkService : IBulkService +{ + readonly Lazy _withRawResponse; + + /// + public IBulkServiceWithRawResponse WithRawResponse + { + get { return _withRawResponse.Value; } + } + + readonly IImageKitClient _client; + + /// + public IBulkService WithOptions(Func modifier) + { + return new BulkService(this._client.WithOptions(modifier)); + } + + public BulkService(IImageKitClient client) + { + _client = client; + + _withRawResponse = new(() => new BulkServiceWithRawResponse(client.WithRawResponse)); + } + + /// + public async Task Delete( + BulkDeleteParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Delete(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public async Task AddTags( + BulkAddTagsParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.AddTags(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public async Task RemoveAITags( + BulkRemoveAITagsParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.RemoveAITags(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public async Task RemoveTags( + BulkRemoveTagsParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.RemoveTags(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } +} + +/// +public sealed class BulkServiceWithRawResponse : IBulkServiceWithRawResponse +{ + readonly IImageKitClientWithRawResponse _client; + + /// + public IBulkServiceWithRawResponse WithOptions(Func modifier) + { + return new BulkServiceWithRawResponse(this._client.WithOptions(modifier)); + } + + public BulkServiceWithRawResponse(IImageKitClientWithRawResponse client) + { + _client = client; + } + + /// + public async Task> Delete( + BulkDeleteParams parameters, + CancellationToken cancellationToken = default + ) + { + HttpRequest request = new() + { + Method = HttpMethod.Post, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var bulk = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + bulk.Validate(); + } + return bulk; + } + ); + } + + /// + public async Task> AddTags( + BulkAddTagsParams parameters, + CancellationToken cancellationToken = default + ) + { + HttpRequest request = new() + { + Method = HttpMethod.Post, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var deserializedResponse = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + deserializedResponse.Validate(); + } + return deserializedResponse; + } + ); + } + + /// + public async Task> RemoveAITags( + BulkRemoveAITagsParams parameters, + CancellationToken cancellationToken = default + ) + { + HttpRequest request = new() + { + Method = HttpMethod.Post, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var deserializedResponse = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + deserializedResponse.Validate(); + } + return deserializedResponse; + } + ); + } + + /// + public async Task> RemoveTags( + BulkRemoveTagsParams parameters, + CancellationToken cancellationToken = default + ) + { + HttpRequest request = new() + { + Method = HttpMethod.Post, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var deserializedResponse = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + deserializedResponse.Validate(); + } + return deserializedResponse; + } + ); + } +} diff --git a/src/Imagekit/Services/Files/IBulkService.cs b/src/Imagekit/Services/Files/IBulkService.cs new file mode 100644 index 00000000..83efe968 --- /dev/null +++ b/src/Imagekit/Services/Files/IBulkService.cs @@ -0,0 +1,119 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Models.Files.Bulk; + +namespace Imagekit.Services.Files; + +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with breaking +/// changes in non-major versions. We may add new methods in the future that cause +/// existing derived classes to break. +/// +public interface IBulkService +{ + /// + /// Returns a view of this service that provides access to raw HTTP responses + /// for each method. + /// + IBulkServiceWithRawResponse WithRawResponse { get; } + + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IBulkService WithOptions(Func modifier); + + /// + /// This API deletes multiple files and all their file versions permanently. + /// + /// Note: If a file or specific transformation has been requested in the past, + /// then the response is cached. Deleting a file does not purge the cache. You can + /// purge the cache using purge cache API. + /// + /// A maximum of 100 files can be deleted at a time. + /// + Task Delete( + BulkDeleteParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// This API adds tags to multiple files in bulk. A maximum of 50 files can be + /// specified at a time. + /// + Task AddTags( + BulkAddTagsParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// This API removes AITags from multiple files in bulk. A maximum of 50 files can + /// be specified at a time. + /// + Task RemoveAITags( + BulkRemoveAITagsParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// This API removes tags from multiple files in bulk. A maximum of 50 files can be + /// specified at a time. + /// + Task RemoveTags( + BulkRemoveTagsParams parameters, + CancellationToken cancellationToken = default + ); +} + +/// +/// A view of that provides access to raw +/// HTTP responses for each method. +/// +public interface IBulkServiceWithRawResponse +{ + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IBulkServiceWithRawResponse WithOptions(Func modifier); + + /// + /// Returns a raw HTTP response for post /v1/files/batch/deleteByFileIds, but is otherwise the + /// same as . + /// + Task> Delete( + BulkDeleteParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for post /v1/files/addTags, but is otherwise the + /// same as . + /// + Task> AddTags( + BulkAddTagsParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for post /v1/files/removeAITags, but is otherwise the + /// same as . + /// + Task> RemoveAITags( + BulkRemoveAITagsParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for post /v1/files/removeTags, but is otherwise the + /// same as . + /// + Task> RemoveTags( + BulkRemoveTagsParams parameters, + CancellationToken cancellationToken = default + ); +} diff --git a/src/Imagekit/Services/Files/IMetadataService.cs b/src/Imagekit/Services/Files/IMetadataService.cs new file mode 100644 index 00000000..3288448b --- /dev/null +++ b/src/Imagekit/Services/Files/IMetadataService.cs @@ -0,0 +1,96 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Models.Files; +using Imagekit.Models.Files.Metadata; + +namespace Imagekit.Services.Files; + +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with breaking +/// changes in non-major versions. We may add new methods in the future that cause +/// existing derived classes to break. +/// +public interface IMetadataService +{ + /// + /// Returns a view of this service that provides access to raw HTTP responses + /// for each method. + /// + IMetadataServiceWithRawResponse WithRawResponse { get; } + + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IMetadataService WithOptions(Func modifier); + + /// + /// You can programmatically get image EXIF, pHash, and other metadata for uploaded + /// files in the ImageKit.io media library using this API. + /// + /// You can also get the metadata in upload API response by passing `metadata` + /// in `responseFields` parameter. + /// + Task Get( + MetadataGetParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task Get( + string fileID, + MetadataGetParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// Get image EXIF, pHash, and other metadata from ImageKit.io powered remote URL + /// using this API. + /// + Task GetFromUrl( + MetadataGetFromUrlParams parameters, + CancellationToken cancellationToken = default + ); +} + +/// +/// A view of that provides access to raw +/// HTTP responses for each method. +/// +public interface IMetadataServiceWithRawResponse +{ + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IMetadataServiceWithRawResponse WithOptions(Func modifier); + + /// + /// Returns a raw HTTP response for get /v1/files/{fileId}/metadata, but is otherwise the + /// same as . + /// + Task> Get( + MetadataGetParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task> Get( + string fileID, + MetadataGetParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for get /v1/metadata, but is otherwise the + /// same as . + /// + Task> GetFromUrl( + MetadataGetFromUrlParams parameters, + CancellationToken cancellationToken = default + ); +} diff --git a/src/Imagekit/Services/Files/IVersionService.cs b/src/Imagekit/Services/Files/IVersionService.cs new file mode 100644 index 00000000..f396d9f9 --- /dev/null +++ b/src/Imagekit/Services/Files/IVersionService.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Models.Files; +using Imagekit.Models.Files.Versions; + +namespace Imagekit.Services.Files; + +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with breaking +/// changes in non-major versions. We may add new methods in the future that cause +/// existing derived classes to break. +/// +public interface IVersionService +{ + /// + /// Returns a view of this service that provides access to raw HTTP responses + /// for each method. + /// + IVersionServiceWithRawResponse WithRawResponse { get; } + + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IVersionService WithOptions(Func modifier); + + /// + /// This API returns details of all versions of a file. + /// + Task> List( + VersionListParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task> List( + string fileID, + VersionListParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// This API deletes a non-current file version permanently. The API returns an + /// empty response. + /// + /// Note: If you want to delete all versions of a file, use the delete file + /// API. + /// + Task Delete( + VersionDeleteParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task Delete( + string versionID, + VersionDeleteParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// This API returns an object with details or attributes of a file version. + /// + Task Get(VersionGetParams parameters, CancellationToken cancellationToken = default); + + /// + Task Get( + string versionID, + VersionGetParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// This API restores a file version as the current file version. + /// + Task Restore( + VersionRestoreParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task Restore( + string versionID, + VersionRestoreParams parameters, + CancellationToken cancellationToken = default + ); +} + +/// +/// A view of that provides access to raw +/// HTTP responses for each method. +/// +public interface IVersionServiceWithRawResponse +{ + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IVersionServiceWithRawResponse WithOptions(Func modifier); + + /// + /// Returns a raw HTTP response for get /v1/files/{fileId}/versions, but is otherwise the + /// same as . + /// + Task>> List( + VersionListParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task>> List( + string fileID, + VersionListParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for delete /v1/files/{fileId}/versions/{versionId}, but is otherwise the + /// same as . + /// + Task> Delete( + VersionDeleteParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task> Delete( + string versionID, + VersionDeleteParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for get /v1/files/{fileId}/versions/{versionId}, but is otherwise the + /// same as . + /// + Task> Get( + VersionGetParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task> Get( + string versionID, + VersionGetParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for put /v1/files/{fileId}/versions/{versionId}/restore, but is otherwise the + /// same as . + /// + Task> Restore( + VersionRestoreParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task> Restore( + string versionID, + VersionRestoreParams parameters, + CancellationToken cancellationToken = default + ); +} diff --git a/src/Imagekit/Services/Files/MetadataService.cs b/src/Imagekit/Services/Files/MetadataService.cs new file mode 100644 index 00000000..a0a7ffc2 --- /dev/null +++ b/src/Imagekit/Services/Files/MetadataService.cs @@ -0,0 +1,163 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Files; +using Imagekit.Models.Files.Metadata; + +namespace Imagekit.Services.Files; + +/// +public sealed class MetadataService : IMetadataService +{ + readonly Lazy _withRawResponse; + + /// + public IMetadataServiceWithRawResponse WithRawResponse + { + get { return _withRawResponse.Value; } + } + + readonly IImageKitClient _client; + + /// + public IMetadataService WithOptions(Func modifier) + { + return new MetadataService(this._client.WithOptions(modifier)); + } + + public MetadataService(IImageKitClient client) + { + _client = client; + + _withRawResponse = new(() => new MetadataServiceWithRawResponse(client.WithRawResponse)); + } + + /// + public async Task Get( + MetadataGetParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Get(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public Task Get( + string fileID, + MetadataGetParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Get(parameters with { FileID = fileID }, cancellationToken); + } + + /// + public async Task GetFromUrl( + MetadataGetFromUrlParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.GetFromUrl(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } +} + +/// +public sealed class MetadataServiceWithRawResponse : IMetadataServiceWithRawResponse +{ + readonly IImageKitClientWithRawResponse _client; + + /// + public IMetadataServiceWithRawResponse WithOptions(Func modifier) + { + return new MetadataServiceWithRawResponse(this._client.WithOptions(modifier)); + } + + public MetadataServiceWithRawResponse(IImageKitClientWithRawResponse client) + { + _client = client; + } + + /// + public async Task> Get( + MetadataGetParams parameters, + CancellationToken cancellationToken = default + ) + { + if (parameters.FileID == null) + { + throw new ImageKitInvalidDataException("'parameters.FileID' cannot be null"); + } + + HttpRequest request = new() + { + Method = HttpMethod.Get, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var metadata = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + metadata.Validate(); + } + return metadata; + } + ); + } + + /// + public Task> Get( + string fileID, + MetadataGetParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Get(parameters with { FileID = fileID }, cancellationToken); + } + + /// + public async Task> GetFromUrl( + MetadataGetFromUrlParams parameters, + CancellationToken cancellationToken = default + ) + { + HttpRequest request = new() + { + Method = HttpMethod.Get, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var metadata = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + metadata.Validate(); + } + return metadata; + } + ); + } +} diff --git a/src/Imagekit/Services/Files/VersionService.cs b/src/Imagekit/Services/Files/VersionService.cs new file mode 100644 index 00000000..7c7868ad --- /dev/null +++ b/src/Imagekit/Services/Files/VersionService.cs @@ -0,0 +1,316 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Files; +using Imagekit.Models.Files.Versions; + +namespace Imagekit.Services.Files; + +/// +public sealed class VersionService : IVersionService +{ + readonly Lazy _withRawResponse; + + /// + public IVersionServiceWithRawResponse WithRawResponse + { + get { return _withRawResponse.Value; } + } + + readonly IImageKitClient _client; + + /// + public IVersionService WithOptions(Func modifier) + { + return new VersionService(this._client.WithOptions(modifier)); + } + + public VersionService(IImageKitClient client) + { + _client = client; + + _withRawResponse = new(() => new VersionServiceWithRawResponse(client.WithRawResponse)); + } + + /// + public async Task> List( + VersionListParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.List(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public Task> List( + string fileID, + VersionListParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.List(parameters with { FileID = fileID }, cancellationToken); + } + + /// + public async Task Delete( + VersionDeleteParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Delete(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public Task Delete( + string versionID, + VersionDeleteParams parameters, + CancellationToken cancellationToken = default + ) + { + return this.Delete(parameters with { VersionID = versionID }, cancellationToken); + } + + /// + public async Task Get( + VersionGetParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Get(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public Task Get( + string versionID, + VersionGetParams parameters, + CancellationToken cancellationToken = default + ) + { + return this.Get(parameters with { VersionID = versionID }, cancellationToken); + } + + /// + public async Task Restore( + VersionRestoreParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Restore(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public Task Restore( + string versionID, + VersionRestoreParams parameters, + CancellationToken cancellationToken = default + ) + { + return this.Restore(parameters with { VersionID = versionID }, cancellationToken); + } +} + +/// +public sealed class VersionServiceWithRawResponse : IVersionServiceWithRawResponse +{ + readonly IImageKitClientWithRawResponse _client; + + /// + public IVersionServiceWithRawResponse WithOptions(Func modifier) + { + return new VersionServiceWithRawResponse(this._client.WithOptions(modifier)); + } + + public VersionServiceWithRawResponse(IImageKitClientWithRawResponse client) + { + _client = client; + } + + /// + public async Task>> List( + VersionListParams parameters, + CancellationToken cancellationToken = default + ) + { + if (parameters.FileID == null) + { + throw new ImageKitInvalidDataException("'parameters.FileID' cannot be null"); + } + + HttpRequest request = new() + { + Method = HttpMethod.Get, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var files = await response.Deserialize>(token).ConfigureAwait(false); + if (this._client.ResponseValidation) + { + foreach (var item in files) + { + item.Validate(); + } + } + return files; + } + ); + } + + /// + public Task>> List( + string fileID, + VersionListParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.List(parameters with { FileID = fileID }, cancellationToken); + } + + /// + public async Task> Delete( + VersionDeleteParams parameters, + CancellationToken cancellationToken = default + ) + { + if (parameters.VersionID == null) + { + throw new ImageKitInvalidDataException("'parameters.VersionID' cannot be null"); + } + + HttpRequest request = new() + { + Method = HttpMethod.Delete, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var version = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + version.Validate(); + } + return version; + } + ); + } + + /// + public Task> Delete( + string versionID, + VersionDeleteParams parameters, + CancellationToken cancellationToken = default + ) + { + return this.Delete(parameters with { VersionID = versionID }, cancellationToken); + } + + /// + public async Task> Get( + VersionGetParams parameters, + CancellationToken cancellationToken = default + ) + { + if (parameters.VersionID == null) + { + throw new ImageKitInvalidDataException("'parameters.VersionID' cannot be null"); + } + + HttpRequest request = new() + { + Method = HttpMethod.Get, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var file = await response.Deserialize(token).ConfigureAwait(false); + if (this._client.ResponseValidation) + { + file.Validate(); + } + return file; + } + ); + } + + /// + public Task> Get( + string versionID, + VersionGetParams parameters, + CancellationToken cancellationToken = default + ) + { + return this.Get(parameters with { VersionID = versionID }, cancellationToken); + } + + /// + public async Task> Restore( + VersionRestoreParams parameters, + CancellationToken cancellationToken = default + ) + { + if (parameters.VersionID == null) + { + throw new ImageKitInvalidDataException("'parameters.VersionID' cannot be null"); + } + + HttpRequest request = new() + { + Method = HttpMethod.Put, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var file = await response.Deserialize(token).ConfigureAwait(false); + if (this._client.ResponseValidation) + { + file.Validate(); + } + return file; + } + ); + } + + /// + public Task> Restore( + string versionID, + VersionRestoreParams parameters, + CancellationToken cancellationToken = default + ) + { + return this.Restore(parameters with { VersionID = versionID }, cancellationToken); + } +} diff --git a/src/Imagekit/Services/FolderService.cs b/src/Imagekit/Services/FolderService.cs new file mode 100644 index 00000000..85b23d89 --- /dev/null +++ b/src/Imagekit/Services/FolderService.cs @@ -0,0 +1,268 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Models.Folders; +using Imagekit.Services.Folders; + +namespace Imagekit.Services; + +/// +public sealed class FolderService : IFolderService +{ + readonly Lazy _withRawResponse; + + /// + public IFolderServiceWithRawResponse WithRawResponse + { + get { return _withRawResponse.Value; } + } + + readonly IImageKitClient _client; + + /// + public IFolderService WithOptions(Func modifier) + { + return new FolderService(this._client.WithOptions(modifier)); + } + + public FolderService(IImageKitClient client) + { + _client = client; + + _withRawResponse = new(() => new FolderServiceWithRawResponse(client.WithRawResponse)); + _job = new(() => new JobService(client)); + } + + readonly Lazy _job; + public IJobService Job + { + get { return _job.Value; } + } + + /// + public async Task Create( + FolderCreateParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Create(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public async Task Delete( + FolderDeleteParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Delete(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public async Task Copy( + FolderCopyParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Copy(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public async Task Move( + FolderMoveParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Move(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public async Task Rename( + FolderRenameParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Rename(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } +} + +/// +public sealed class FolderServiceWithRawResponse : IFolderServiceWithRawResponse +{ + readonly IImageKitClientWithRawResponse _client; + + /// + public IFolderServiceWithRawResponse WithOptions(Func modifier) + { + return new FolderServiceWithRawResponse(this._client.WithOptions(modifier)); + } + + public FolderServiceWithRawResponse(IImageKitClientWithRawResponse client) + { + _client = client; + + _job = new(() => new JobServiceWithRawResponse(client)); + } + + readonly Lazy _job; + public IJobServiceWithRawResponse Job + { + get { return _job.Value; } + } + + /// + public async Task> Create( + FolderCreateParams parameters, + CancellationToken cancellationToken = default + ) + { + HttpRequest request = new() + { + Method = HttpMethod.Post, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var folder = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + folder.Validate(); + } + return folder; + } + ); + } + + /// + public async Task> Delete( + FolderDeleteParams parameters, + CancellationToken cancellationToken = default + ) + { + HttpRequest request = new() + { + Method = HttpMethod.Delete, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var folder = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + folder.Validate(); + } + return folder; + } + ); + } + + /// + public async Task> Copy( + FolderCopyParams parameters, + CancellationToken cancellationToken = default + ) + { + HttpRequest request = new() + { + Method = HttpMethod.Post, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var deserializedResponse = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + deserializedResponse.Validate(); + } + return deserializedResponse; + } + ); + } + + /// + public async Task> Move( + FolderMoveParams parameters, + CancellationToken cancellationToken = default + ) + { + HttpRequest request = new() + { + Method = HttpMethod.Post, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var deserializedResponse = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + deserializedResponse.Validate(); + } + return deserializedResponse; + } + ); + } + + /// + public async Task> Rename( + FolderRenameParams parameters, + CancellationToken cancellationToken = default + ) + { + HttpRequest request = new() + { + Method = HttpMethod.Post, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var deserializedResponse = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + deserializedResponse.Validate(); + } + return deserializedResponse; + } + ); + } +} diff --git a/src/Imagekit/Services/Folders/IJobService.cs b/src/Imagekit/Services/Folders/IJobService.cs new file mode 100644 index 00000000..7c2a7f75 --- /dev/null +++ b/src/Imagekit/Services/Folders/IJobService.cs @@ -0,0 +1,73 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Models.Folders.Job; + +namespace Imagekit.Services.Folders; + +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with breaking +/// changes in non-major versions. We may add new methods in the future that cause +/// existing derived classes to break. +/// +public interface IJobService +{ + /// + /// Returns a view of this service that provides access to raw HTTP responses + /// for each method. + /// + IJobServiceWithRawResponse WithRawResponse { get; } + + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IJobService WithOptions(Func modifier); + + /// + /// This API returns the status of a bulk job like copy and move folder operations. + /// + Task Get( + JobGetParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task Get( + string jobID, + JobGetParams? parameters = null, + CancellationToken cancellationToken = default + ); +} + +/// +/// A view of that provides access to raw +/// HTTP responses for each method. +/// +public interface IJobServiceWithRawResponse +{ + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IJobServiceWithRawResponse WithOptions(Func modifier); + + /// + /// Returns a raw HTTP response for get /v1/bulkJobs/{jobId}, but is otherwise the + /// same as . + /// + Task> Get( + JobGetParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task> Get( + string jobID, + JobGetParams? parameters = null, + CancellationToken cancellationToken = default + ); +} diff --git a/src/Imagekit/Services/Folders/JobService.cs b/src/Imagekit/Services/Folders/JobService.cs new file mode 100644 index 00000000..0ea7f61d --- /dev/null +++ b/src/Imagekit/Services/Folders/JobService.cs @@ -0,0 +1,116 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.Folders.Job; + +namespace Imagekit.Services.Folders; + +/// +public sealed class JobService : IJobService +{ + readonly Lazy _withRawResponse; + + /// + public IJobServiceWithRawResponse WithRawResponse + { + get { return _withRawResponse.Value; } + } + + readonly IImageKitClient _client; + + /// + public IJobService WithOptions(Func modifier) + { + return new JobService(this._client.WithOptions(modifier)); + } + + public JobService(IImageKitClient client) + { + _client = client; + + _withRawResponse = new(() => new JobServiceWithRawResponse(client.WithRawResponse)); + } + + /// + public async Task Get( + JobGetParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Get(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public Task Get( + string jobID, + JobGetParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Get(parameters with { JobID = jobID }, cancellationToken); + } +} + +/// +public sealed class JobServiceWithRawResponse : IJobServiceWithRawResponse +{ + readonly IImageKitClientWithRawResponse _client; + + /// + public IJobServiceWithRawResponse WithOptions(Func modifier) + { + return new JobServiceWithRawResponse(this._client.WithOptions(modifier)); + } + + public JobServiceWithRawResponse(IImageKitClientWithRawResponse client) + { + _client = client; + } + + /// + public async Task> Get( + JobGetParams parameters, + CancellationToken cancellationToken = default + ) + { + if (parameters.JobID == null) + { + throw new ImageKitInvalidDataException("'parameters.JobID' cannot be null"); + } + + HttpRequest request = new() { Method = HttpMethod.Get, Params = parameters }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var job = await response.Deserialize(token).ConfigureAwait(false); + if (this._client.ResponseValidation) + { + job.Validate(); + } + return job; + } + ); + } + + /// + public Task> Get( + string jobID, + JobGetParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Get(parameters with { JobID = jobID }, cancellationToken); + } +} diff --git a/src/Imagekit/Services/IAccountService.cs b/src/Imagekit/Services/IAccountService.cs new file mode 100644 index 00000000..9eaf50b2 --- /dev/null +++ b/src/Imagekit/Services/IAccountService.cs @@ -0,0 +1,52 @@ +using System; +using Imagekit.Core; +using Imagekit.Services.Accounts; + +namespace Imagekit.Services; + +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with breaking +/// changes in non-major versions. We may add new methods in the future that cause +/// existing derived classes to break. +/// +public interface IAccountService +{ + /// + /// Returns a view of this service that provides access to raw HTTP responses + /// for each method. + /// + IAccountServiceWithRawResponse WithRawResponse { get; } + + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IAccountService WithOptions(Func modifier); + + IUsageService Usage { get; } + + IOriginService Origins { get; } + + IUrlEndpointService UrlEndpoints { get; } +} + +/// +/// A view of that provides access to raw +/// HTTP responses for each method. +/// +public interface IAccountServiceWithRawResponse +{ + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IAccountServiceWithRawResponse WithOptions(Func modifier); + + IUsageServiceWithRawResponse Usage { get; } + + IOriginServiceWithRawResponse Origins { get; } + + IUrlEndpointServiceWithRawResponse UrlEndpoints { get; } +} diff --git a/src/Imagekit/Services/IAssetService.cs b/src/Imagekit/Services/IAssetService.cs new file mode 100644 index 00000000..b58d0e48 --- /dev/null +++ b/src/Imagekit/Services/IAssetService.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Models.Assets; + +namespace Imagekit.Services; + +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with breaking +/// changes in non-major versions. We may add new methods in the future that cause +/// existing derived classes to break. +/// +public interface IAssetService +{ + /// + /// Returns a view of this service that provides access to raw HTTP responses + /// for each method. + /// + IAssetServiceWithRawResponse WithRawResponse { get; } + + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IAssetService WithOptions(Func modifier); + + /// + /// This API can list all the uploaded files and folders in your ImageKit.io media + /// library. In addition, you can fine-tune your query by specifying various filters + /// by generating a query string in a Lucene-like syntax and provide this generated + /// string as the value of the `searchQuery`. + /// + Task> List( + AssetListParams? parameters = null, + CancellationToken cancellationToken = default + ); +} + +/// +/// A view of that provides access to raw +/// HTTP responses for each method. +/// +public interface IAssetServiceWithRawResponse +{ + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IAssetServiceWithRawResponse WithOptions(Func modifier); + + /// + /// Returns a raw HTTP response for get /v1/files, but is otherwise the + /// same as . + /// + Task>> List( + AssetListParams? parameters = null, + CancellationToken cancellationToken = default + ); +} diff --git a/src/Imagekit/Services/IBetaService.cs b/src/Imagekit/Services/IBetaService.cs new file mode 100644 index 00000000..dafe3f60 --- /dev/null +++ b/src/Imagekit/Services/IBetaService.cs @@ -0,0 +1,44 @@ +using System; +using Imagekit.Core; +using Imagekit.Services.Beta; + +namespace Imagekit.Services; + +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with breaking +/// changes in non-major versions. We may add new methods in the future that cause +/// existing derived classes to break. +/// +public interface IBetaService +{ + /// + /// Returns a view of this service that provides access to raw HTTP responses + /// for each method. + /// + IBetaServiceWithRawResponse WithRawResponse { get; } + + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IBetaService WithOptions(Func modifier); + + IV2Service V2 { get; } +} + +/// +/// A view of that provides access to raw +/// HTTP responses for each method. +/// +public interface IBetaServiceWithRawResponse +{ + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IBetaServiceWithRawResponse WithOptions(Func modifier); + + IV2ServiceWithRawResponse V2 { get; } +} diff --git a/src/Imagekit/Services/ICacheService.cs b/src/Imagekit/Services/ICacheService.cs new file mode 100644 index 00000000..e6f3b2dc --- /dev/null +++ b/src/Imagekit/Services/ICacheService.cs @@ -0,0 +1,44 @@ +using System; +using Imagekit.Core; +using Imagekit.Services.Cache; + +namespace Imagekit.Services; + +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with breaking +/// changes in non-major versions. We may add new methods in the future that cause +/// existing derived classes to break. +/// +public interface ICacheService +{ + /// + /// Returns a view of this service that provides access to raw HTTP responses + /// for each method. + /// + ICacheServiceWithRawResponse WithRawResponse { get; } + + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + ICacheService WithOptions(Func modifier); + + IInvalidationService Invalidation { get; } +} + +/// +/// A view of that provides access to raw +/// HTTP responses for each method. +/// +public interface ICacheServiceWithRawResponse +{ + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + ICacheServiceWithRawResponse WithOptions(Func modifier); + + IInvalidationServiceWithRawResponse Invalidation { get; } +} diff --git a/src/Imagekit/Services/ICustomMetadataFieldService.cs b/src/Imagekit/Services/ICustomMetadataFieldService.cs new file mode 100644 index 00000000..af822b15 --- /dev/null +++ b/src/Imagekit/Services/ICustomMetadataFieldService.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Models.CustomMetadataFields; + +namespace Imagekit.Services; + +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with breaking +/// changes in non-major versions. We may add new methods in the future that cause +/// existing derived classes to break. +/// +public interface ICustomMetadataFieldService +{ + /// + /// Returns a view of this service that provides access to raw HTTP responses + /// for each method. + /// + ICustomMetadataFieldServiceWithRawResponse WithRawResponse { get; } + + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + ICustomMetadataFieldService WithOptions(Func modifier); + + /// + /// This API creates a new custom metadata field. Once a custom metadata field is + /// created either through this API or using the dashboard UI, its value can be set + /// on the assets. The value of a field for an asset can be set using the media + /// library UI or programmatically through upload or update assets API. + /// + Task Create( + CustomMetadataFieldCreateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// This API updates the label or schema of an existing custom metadata field. + /// + Task Update( + CustomMetadataFieldUpdateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task Update( + string id, + CustomMetadataFieldUpdateParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// This API returns the array of created custom metadata field objects. By default + /// the API returns only non deleted field objects, but you can include deleted + /// fields in the API response. + /// + /// You can also filter results by a specific folder path to retrieve custom + /// metadata fields applicable at that location. This path-specific filtering is + /// useful when using the **Path policy** feature to determine which custom metadata + /// fields are selected for a given path. + /// + Task> List( + CustomMetadataFieldListParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// This API deletes a custom metadata field. Even after deleting a custom metadata + /// field, you cannot create any new custom metadata field with the same name. + /// + Task Delete( + CustomMetadataFieldDeleteParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task Delete( + string id, + CustomMetadataFieldDeleteParams? parameters = null, + CancellationToken cancellationToken = default + ); +} + +/// +/// A view of that provides access to raw +/// HTTP responses for each method. +/// +public interface ICustomMetadataFieldServiceWithRawResponse +{ + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + ICustomMetadataFieldServiceWithRawResponse WithOptions( + Func modifier + ); + + /// + /// Returns a raw HTTP response for post /v1/customMetadataFields, but is otherwise the + /// same as . + /// + Task> Create( + CustomMetadataFieldCreateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for patch /v1/customMetadataFields/{id}, but is otherwise the + /// same as . + /// + Task> Update( + CustomMetadataFieldUpdateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task> Update( + string id, + CustomMetadataFieldUpdateParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for get /v1/customMetadataFields, but is otherwise the + /// same as . + /// + Task>> List( + CustomMetadataFieldListParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for delete /v1/customMetadataFields/{id}, but is otherwise the + /// same as . + /// + Task> Delete( + CustomMetadataFieldDeleteParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task> Delete( + string id, + CustomMetadataFieldDeleteParams? parameters = null, + CancellationToken cancellationToken = default + ); +} diff --git a/src/Imagekit/Services/IDummyService.cs b/src/Imagekit/Services/IDummyService.cs new file mode 100644 index 00000000..bf9147df --- /dev/null +++ b/src/Imagekit/Services/IDummyService.cs @@ -0,0 +1,61 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Models.Dummy; + +namespace Imagekit.Services; + +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with breaking +/// changes in non-major versions. We may add new methods in the future that cause +/// existing derived classes to break. +/// +public interface IDummyService +{ + /// + /// Returns a view of this service that provides access to raw HTTP responses + /// for each method. + /// + IDummyServiceWithRawResponse WithRawResponse { get; } + + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IDummyService WithOptions(Func modifier); + + /// + /// Internal test endpoint for SDK generation purposes only. This endpoint + /// demonstrates usage of all shared models defined in the Stainless configuration + /// and is not intended for public consumption. + /// + Task Create( + DummyCreateParams? parameters = null, + CancellationToken cancellationToken = default + ); +} + +/// +/// A view of that provides access to raw +/// HTTP responses for each method. +/// +public interface IDummyServiceWithRawResponse +{ + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IDummyServiceWithRawResponse WithOptions(Func modifier); + + /// + /// Returns a raw HTTP response for post /v1/dummy/test, but is otherwise the + /// same as . + /// + Task Create( + DummyCreateParams? parameters = null, + CancellationToken cancellationToken = default + ); +} diff --git a/src/Imagekit/Services/IFileService.cs b/src/Imagekit/Services/IFileService.cs new file mode 100644 index 00000000..3b26433b --- /dev/null +++ b/src/Imagekit/Services/IFileService.cs @@ -0,0 +1,253 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Models.Files; +using Imagekit.Services.Files; + +namespace Imagekit.Services; + +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with breaking +/// changes in non-major versions. We may add new methods in the future that cause +/// existing derived classes to break. +/// +public interface IFileService +{ + /// + /// Returns a view of this service that provides access to raw HTTP responses + /// for each method. + /// + IFileServiceWithRawResponse WithRawResponse { get; } + + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IFileService WithOptions(Func modifier); + + IBulkService Bulk { get; } + + IVersionService Versions { get; } + + IMetadataService Metadata { get; } + + /// + /// This API updates the details or attributes of the current version of the file. + /// You can update `tags`, `customCoordinates`, `customMetadata`, publication + /// status, remove existing `AITags` and apply extensions using this API. + /// + Task Update( + FileUpdateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task Update( + string fileID, + FileUpdateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// This API deletes the file and all its file versions permanently. + /// + /// Note: If a file or specific transformation has been requested in the past, + /// then the response is cached. Deleting a file does not purge the cache. You can + /// purge the cache using purge cache API. + /// + Task Delete(FileDeleteParams parameters, CancellationToken cancellationToken = default); + + /// + Task Delete( + string fileID, + FileDeleteParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// This will copy a file from one folder to another. + /// + /// Note: If any file at the destination has the same name as the source file, + /// then the source file and its versions (if `includeFileVersions` is set to true) + /// will be appended to the destination file version history. + /// + Task Copy( + FileCopyParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// This API returns an object with details or attributes about the current version + /// of the file. + /// + Task Get(FileGetParams parameters, CancellationToken cancellationToken = default); + + /// + Task Get( + string fileID, + FileGetParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// This will move a file and all its versions from one folder to another. + /// + /// Note: If any file at the destination has the same name as the source file, + /// then the source file and its versions will be appended to the destination file. + /// + Task Move( + FileMoveParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// You can rename an already existing file in the media library using rename file + /// API. This operation would rename all file versions of the file. + /// + /// Note: The old URLs will stop working. The file/file version URLs cached on + /// CDN will continue to work unless a purge is requested. + /// + Task Rename( + FileRenameParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// ImageKit.io allows you to upload files directly from both the server and client + /// sides. For server-side uploads, private API key authentication is used. For + /// client-side uploads, generate a one-time `token`, `signature`, and `expire` from + /// your secure backend using private API. [Learn + /// more](/docs/api-reference/upload-file/upload-file#how-to-implement-client-side-file-upload) + /// about how to implement client-side file upload. + /// + /// The [V2 API](/docs/api-reference/upload-file/upload-file-v2) enhances + /// security by verifying the entire payload using JWT. + /// + /// **File size limit** \ On the free plan, the maximum upload file sizes are + /// 25MB for images, audio, and raw files and 100MB for videos. On the Lite paid + /// plan, these limits increase to 40MB for images, audio, and raw files and 300MB + /// for videos, whereas on the Pro paid plan, these limits increase to 50MB for + /// images, audio, and raw files and 2GB for videos. These limits can be further + /// increased with enterprise plans. + /// + /// **Version limit** \ A file can have a maximum of 100 versions. + /// + /// **Demo applications** + /// + /// - A full-fledged [upload widget using + /// Uppy](https://github.com/imagekit-samples/uppy-uploader), supporting file + /// selections from local storage, URL, Dropbox, Google Drive, Instagram, and more. + /// - [Quick start guides](/docs/quick-start-guides) for various frameworks and + /// technologies. + /// + Task Upload( + FileUploadParams parameters, + CancellationToken cancellationToken = default + ); +} + +/// +/// A view of that provides access to raw +/// HTTP responses for each method. +/// +public interface IFileServiceWithRawResponse +{ + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IFileServiceWithRawResponse WithOptions(Func modifier); + + IBulkServiceWithRawResponse Bulk { get; } + + IVersionServiceWithRawResponse Versions { get; } + + IMetadataServiceWithRawResponse Metadata { get; } + + /// + /// Returns a raw HTTP response for patch /v1/files/{fileId}/details, but is otherwise the + /// same as . + /// + Task> Update( + FileUpdateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task> Update( + string fileID, + FileUpdateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for delete /v1/files/{fileId}, but is otherwise the + /// same as . + /// + Task Delete( + FileDeleteParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task Delete( + string fileID, + FileDeleteParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for post /v1/files/copy, but is otherwise the + /// same as . + /// + Task> Copy( + FileCopyParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for get /v1/files/{fileId}/details, but is otherwise the + /// same as . + /// + Task> Get( + FileGetParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task> Get( + string fileID, + FileGetParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for post /v1/files/move, but is otherwise the + /// same as . + /// + Task> Move( + FileMoveParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for put /v1/files/rename, but is otherwise the + /// same as . + /// + Task> Rename( + FileRenameParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for post /api/v1/files/upload, but is otherwise the + /// same as . + /// + Task> Upload( + FileUploadParams parameters, + CancellationToken cancellationToken = default + ); +} diff --git a/src/Imagekit/Services/IFolderService.cs b/src/Imagekit/Services/IFolderService.cs new file mode 100644 index 00000000..ce3708d0 --- /dev/null +++ b/src/Imagekit/Services/IFolderService.cs @@ -0,0 +1,143 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Models.Folders; +using Imagekit.Services.Folders; + +namespace Imagekit.Services; + +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with breaking +/// changes in non-major versions. We may add new methods in the future that cause +/// existing derived classes to break. +/// +public interface IFolderService +{ + /// + /// Returns a view of this service that provides access to raw HTTP responses + /// for each method. + /// + IFolderServiceWithRawResponse WithRawResponse { get; } + + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IFolderService WithOptions(Func modifier); + + IJobService Job { get; } + + /// + /// This will create a new folder. You can specify the folder name and location of + /// the parent folder where this new folder should be created. + /// + Task Create( + FolderCreateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// This will delete a folder and all its contents permanently. The API returns an + /// empty response. + /// + Task Delete( + FolderDeleteParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// This will copy one folder into another. The selected folder, its nested folders, + /// files, and their versions (in `includeVersions` is set to true) are copied in + /// this operation. Note: If any file at the destination has the same name as the + /// source file, then the source file and its versions will be appended to the + /// destination file version history. + /// + Task Copy( + FolderCopyParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// This will move one folder into another. The selected folder, its nested folders, + /// files, and their versions are moved in this operation. Note: If any file at the + /// destination has the same name as the source file, then the source file and its + /// versions will be appended to the destination file version history. + /// + Task Move( + FolderMoveParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// This API allows you to rename an existing folder. The folder and all its nested + /// assets and sub-folders will remain unchanged, but their paths will be updated to + /// reflect the new folder name. + /// + Task Rename( + FolderRenameParams parameters, + CancellationToken cancellationToken = default + ); +} + +/// +/// A view of that provides access to raw +/// HTTP responses for each method. +/// +public interface IFolderServiceWithRawResponse +{ + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IFolderServiceWithRawResponse WithOptions(Func modifier); + + IJobServiceWithRawResponse Job { get; } + + /// + /// Returns a raw HTTP response for post /v1/folder, but is otherwise the + /// same as . + /// + Task> Create( + FolderCreateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for delete /v1/folder, but is otherwise the + /// same as . + /// + Task> Delete( + FolderDeleteParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for post /v1/bulkJobs/copyFolder, but is otherwise the + /// same as . + /// + Task> Copy( + FolderCopyParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for post /v1/bulkJobs/moveFolder, but is otherwise the + /// same as . + /// + Task> Move( + FolderMoveParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for post /v1/bulkJobs/renameFolder, but is otherwise the + /// same as . + /// + Task> Rename( + FolderRenameParams parameters, + CancellationToken cancellationToken = default + ); +} diff --git a/src/Imagekit/Services/ISavedExtensionService.cs b/src/Imagekit/Services/ISavedExtensionService.cs new file mode 100644 index 00000000..fca28849 --- /dev/null +++ b/src/Imagekit/Services/ISavedExtensionService.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Models.SavedExtensions; +using Models = Imagekit.Models; + +namespace Imagekit.Services; + +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with breaking +/// changes in non-major versions. We may add new methods in the future that cause +/// existing derived classes to break. +/// +public interface ISavedExtensionService +{ + /// + /// Returns a view of this service that provides access to raw HTTP responses + /// for each method. + /// + ISavedExtensionServiceWithRawResponse WithRawResponse { get; } + + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + ISavedExtensionService WithOptions(Func modifier); + + /// + /// This API creates a new saved extension. Saved extensions allow you to save + /// complex extension configurations (like AI tasks) and reuse them by referencing + /// the ID in upload or update file APIs. + /// + /// **Saved extension limit** \ You can create a maximum of 100 saved + /// extensions per account. + /// + Task Create( + SavedExtensionCreateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// This API updates an existing saved extension. You can update the name, + /// description, or config. + /// + Task Update( + SavedExtensionUpdateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task Update( + string id, + SavedExtensionUpdateParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// This API returns an array of all saved extensions for your account. Saved + /// extensions allow you to save complex extension configurations and reuse them by + /// referencing them by ID in upload or update file APIs. + /// + Task> List( + SavedExtensionListParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// This API deletes a saved extension permanently. + /// + Task Delete( + SavedExtensionDeleteParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task Delete( + string id, + SavedExtensionDeleteParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// This API returns details of a specific saved extension by ID. + /// + Task Get( + SavedExtensionGetParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task Get( + string id, + SavedExtensionGetParams? parameters = null, + CancellationToken cancellationToken = default + ); +} + +/// +/// A view of that provides access to raw +/// HTTP responses for each method. +/// +public interface ISavedExtensionServiceWithRawResponse +{ + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + ISavedExtensionServiceWithRawResponse WithOptions(Func modifier); + + /// + /// Returns a raw HTTP response for post /v1/saved-extensions, but is otherwise the + /// same as . + /// + Task> Create( + SavedExtensionCreateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for patch /v1/saved-extensions/{id}, but is otherwise the + /// same as . + /// + Task> Update( + SavedExtensionUpdateParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task> Update( + string id, + SavedExtensionUpdateParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for get /v1/saved-extensions, but is otherwise the + /// same as . + /// + Task>> List( + SavedExtensionListParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for delete /v1/saved-extensions/{id}, but is otherwise the + /// same as . + /// + Task Delete( + SavedExtensionDeleteParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task Delete( + string id, + SavedExtensionDeleteParams? parameters = null, + CancellationToken cancellationToken = default + ); + + /// + /// Returns a raw HTTP response for get /v1/saved-extensions/{id}, but is otherwise the + /// same as . + /// + Task> Get( + SavedExtensionGetParams parameters, + CancellationToken cancellationToken = default + ); + + /// + Task> Get( + string id, + SavedExtensionGetParams? parameters = null, + CancellationToken cancellationToken = default + ); +} diff --git a/src/Imagekit/Services/IWebhookService.cs b/src/Imagekit/Services/IWebhookService.cs new file mode 100644 index 00000000..aa79aac5 --- /dev/null +++ b/src/Imagekit/Services/IWebhookService.cs @@ -0,0 +1,39 @@ +using System; +using Imagekit.Core; + +namespace Imagekit.Services; + +/// +/// NOTE: Do not inherit from this type outside the SDK unless you're okay with breaking +/// changes in non-major versions. We may add new methods in the future that cause +/// existing derived classes to break. +/// +public interface IWebhookService +{ + /// + /// Returns a view of this service that provides access to raw HTTP responses + /// for each method. + /// + IWebhookServiceWithRawResponse WithRawResponse { get; } + + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IWebhookService WithOptions(Func modifier); +} + +/// +/// A view of that provides access to raw +/// HTTP responses for each method. +/// +public interface IWebhookServiceWithRawResponse +{ + /// + /// Returns a view of this service with the given option modifications applied. + /// + /// The original service is not modified. + /// + IWebhookServiceWithRawResponse WithOptions(Func modifier); +} diff --git a/src/Imagekit/Services/SavedExtensionService.cs b/src/Imagekit/Services/SavedExtensionService.cs new file mode 100644 index 00000000..f0ec69c1 --- /dev/null +++ b/src/Imagekit/Services/SavedExtensionService.cs @@ -0,0 +1,334 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Imagekit.Core; +using Imagekit.Exceptions; +using Imagekit.Models.SavedExtensions; +using Models = Imagekit.Models; + +namespace Imagekit.Services; + +/// +public sealed class SavedExtensionService : ISavedExtensionService +{ + readonly Lazy _withRawResponse; + + /// + public ISavedExtensionServiceWithRawResponse WithRawResponse + { + get { return _withRawResponse.Value; } + } + + readonly IImageKitClient _client; + + /// + public ISavedExtensionService WithOptions(Func modifier) + { + return new SavedExtensionService(this._client.WithOptions(modifier)); + } + + public SavedExtensionService(IImageKitClient client) + { + _client = client; + + _withRawResponse = new(() => + new SavedExtensionServiceWithRawResponse(client.WithRawResponse) + ); + } + + /// + public async Task Create( + SavedExtensionCreateParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Create(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public async Task Update( + SavedExtensionUpdateParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Update(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public Task Update( + string id, + SavedExtensionUpdateParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Update(parameters with { ID = id }, cancellationToken); + } + + /// + public async Task> List( + SavedExtensionListParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.List(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public Task Delete( + SavedExtensionDeleteParams parameters, + CancellationToken cancellationToken = default + ) + { + return this.WithRawResponse.Delete(parameters, cancellationToken); + } + + /// + public async Task Delete( + string id, + SavedExtensionDeleteParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + await this.Delete(parameters with { ID = id }, cancellationToken).ConfigureAwait(false); + } + + /// + public async Task Get( + SavedExtensionGetParams parameters, + CancellationToken cancellationToken = default + ) + { + using var response = await this + .WithRawResponse.Get(parameters, cancellationToken) + .ConfigureAwait(false); + return await response.Deserialize(cancellationToken).ConfigureAwait(false); + } + + /// + public Task Get( + string id, + SavedExtensionGetParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Get(parameters with { ID = id }, cancellationToken); + } +} + +/// +public sealed class SavedExtensionServiceWithRawResponse : ISavedExtensionServiceWithRawResponse +{ + readonly IImageKitClientWithRawResponse _client; + + /// + public ISavedExtensionServiceWithRawResponse WithOptions( + Func modifier + ) + { + return new SavedExtensionServiceWithRawResponse(this._client.WithOptions(modifier)); + } + + public SavedExtensionServiceWithRawResponse(IImageKitClientWithRawResponse client) + { + _client = client; + } + + /// + public async Task> Create( + SavedExtensionCreateParams parameters, + CancellationToken cancellationToken = default + ) + { + HttpRequest request = new() + { + Method = HttpMethod.Post, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var savedExtension = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + savedExtension.Validate(); + } + return savedExtension; + } + ); + } + + /// + public async Task> Update( + SavedExtensionUpdateParams parameters, + CancellationToken cancellationToken = default + ) + { + if (parameters.ID == null) + { + throw new ImageKitInvalidDataException("'parameters.ID' cannot be null"); + } + + HttpRequest request = new() + { + Method = ImageKitClientWithRawResponse.PatchMethod, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var savedExtension = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + savedExtension.Validate(); + } + return savedExtension; + } + ); + } + + /// + public Task> Update( + string id, + SavedExtensionUpdateParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Update(parameters with { ID = id }, cancellationToken); + } + + /// + public async Task>> List( + SavedExtensionListParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + HttpRequest request = new() + { + Method = HttpMethod.Get, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var savedExtensions = await response + .Deserialize>(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + foreach (var item in savedExtensions) + { + item.Validate(); + } + } + return savedExtensions; + } + ); + } + + /// + public Task Delete( + SavedExtensionDeleteParams parameters, + CancellationToken cancellationToken = default + ) + { + if (parameters.ID == null) + { + throw new ImageKitInvalidDataException("'parameters.ID' cannot be null"); + } + + HttpRequest request = new() + { + Method = HttpMethod.Delete, + Params = parameters, + }; + return this._client.Execute(request, cancellationToken); + } + + /// + public Task Delete( + string id, + SavedExtensionDeleteParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Delete(parameters with { ID = id }, cancellationToken); + } + + /// + public async Task> Get( + SavedExtensionGetParams parameters, + CancellationToken cancellationToken = default + ) + { + if (parameters.ID == null) + { + throw new ImageKitInvalidDataException("'parameters.ID' cannot be null"); + } + + HttpRequest request = new() + { + Method = HttpMethod.Get, + Params = parameters, + }; + var response = await this._client.Execute(request, cancellationToken).ConfigureAwait(false); + return new( + response, + async (token) => + { + var savedExtension = await response + .Deserialize(token) + .ConfigureAwait(false); + if (this._client.ResponseValidation) + { + savedExtension.Validate(); + } + return savedExtension; + } + ); + } + + /// + public Task> Get( + string id, + SavedExtensionGetParams? parameters = null, + CancellationToken cancellationToken = default + ) + { + parameters ??= new(); + + return this.Get(parameters with { ID = id }, cancellationToken); + } +} diff --git a/src/Imagekit/Services/WebhookService.cs b/src/Imagekit/Services/WebhookService.cs new file mode 100644 index 00000000..e4f938c5 --- /dev/null +++ b/src/Imagekit/Services/WebhookService.cs @@ -0,0 +1,48 @@ +using System; +using Imagekit.Core; + +namespace Imagekit.Services; + +/// +public sealed class WebhookService : IWebhookService +{ + readonly Lazy _withRawResponse; + + /// + public IWebhookServiceWithRawResponse WithRawResponse + { + get { return _withRawResponse.Value; } + } + + readonly IImageKitClient _client; + + /// + public IWebhookService WithOptions(Func modifier) + { + return new WebhookService(this._client.WithOptions(modifier)); + } + + public WebhookService(IImageKitClient client) + { + _client = client; + + _withRawResponse = new(() => new WebhookServiceWithRawResponse(client.WithRawResponse)); + } +} + +/// +public sealed class WebhookServiceWithRawResponse : IWebhookServiceWithRawResponse +{ + readonly IImageKitClientWithRawResponse _client; + + /// + public IWebhookServiceWithRawResponse WithOptions(Func modifier) + { + return new WebhookServiceWithRawResponse(this._client.WithOptions(modifier)); + } + + public WebhookServiceWithRawResponse(IImageKitClientWithRawResponse client) + { + _client = client; + } +} diff --git a/src/Imagekit/Shims.cs b/src/Imagekit/Shims.cs new file mode 100644 index 00000000..c93321fb --- /dev/null +++ b/src/Imagekit/Shims.cs @@ -0,0 +1,50 @@ +using System; + +#if !NET +#pragma warning disable IDE0130 // Namespace does not match folder structure +#pragma warning disable CS9113 // Unused parameters + +namespace System.Runtime.CompilerServices +{ + // Allow `required` to compile when targeting .NET Standard 2.0. + [AttributeUsage( + AttributeTargets.Class + | AttributeTargets.Struct + | AttributeTargets.Property + | AttributeTargets.Field, + AllowMultiple = false, + Inherited = false + )] + internal sealed class RequiredMemberAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)] + internal sealed class CompilerFeatureRequiredAttribute : Attribute + { + public CompilerFeatureRequiredAttribute(string feature) { } + } + + // Allow `init` to compile when targeting .NET Standard 2.0. + internal static class IsExternalInit { } +} + +namespace System.Diagnostics.CodeAnalysis +{ + // Allow `[SetsRequiredMembers]` to compile when targeting .NET Standard 2.0. + [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)] + internal sealed class SetsRequiredMembersAttribute : Attribute { } + + // Allow `[MaybeNullWhen(...)]` to compile when targeting .NET Standard 2.0. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class MaybeNullWhenAttribute : Attribute + { + public MaybeNullWhenAttribute(bool returnValue) { } + } + + // Allow `[NotNullWhen(...)]` to compile when targeting .NET Standard 2.0. + [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] + internal sealed class NotNullWhenAttribute : Attribute + { + public NotNullWhenAttribute(bool returnValue) { } + } +} +#endif